summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xcore/java/android/provider/Settings.java8
-rw-r--r--core/java/com/android/internal/policy/EmergencyAffordanceManager.java101
-rw-r--r--core/res/res/drawable/emergency_icon.xml40
-rw-r--r--core/res/res/values/config.xml10
-rw-r--r--core/res/res/values/strings.xml3
-rw-r--r--core/res/res/values/symbols.xml6
-rw-r--r--packages/Keyguard/src/com/android/keyguard/EmergencyButton.java43
-rw-r--r--services/core/java/com/android/server/emergency/EmergencyAffordanceService.java312
-rw-r--r--services/core/java/com/android/server/policy/GlobalActions.java29
-rw-r--r--services/java/com/android/server/SystemServer.java9
10 files changed, 557 insertions, 4 deletions
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index e91af140581b..ed4675f57712 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -44,7 +44,6 @@ import android.net.ConnectivityManager;
import android.net.Uri;
import android.net.wifi.WifiManager;
import android.os.BatteryManager;
-import android.os.Binder;
import android.os.Bundle;
import android.os.DropBoxManager;
import android.os.IBinder;
@@ -8372,6 +8371,13 @@ public final class Settings {
public static final String CALL_AUTO_RETRY = "call_auto_retry";
/**
+ * A setting that can be read whether the emergency affordance is currently needed.
+ * The value is a boolean (1 or 0).
+ * @hide
+ */
+ public static final String EMERGENCY_AFFORDANCE_NEEDED = "emergency_affordance_needed";
+
+ /**
* See RIL_PreferredNetworkType in ril.h
* @hide
*/
diff --git a/core/java/com/android/internal/policy/EmergencyAffordanceManager.java b/core/java/com/android/internal/policy/EmergencyAffordanceManager.java
new file mode 100644
index 000000000000..bed7c1ba4ed3
--- /dev/null
+++ b/core/java/com/android/internal/policy/EmergencyAffordanceManager.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.internal.policy;
+
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Build;
+import android.provider.Settings;
+
+/**
+ * A class that manages emergency affordances and enables immediate calling to emergency services
+ */
+public class EmergencyAffordanceManager {
+
+ public static final boolean ENABLED = true;
+
+ /**
+ * Global setting override with the number to call with the emergency affordance.
+ * @hide
+ */
+ private static final String EMERGENCY_CALL_NUMBER_SETTING = "emergency_affordance_number";
+
+ /**
+ * Global setting, whether the emergency affordance should be shown regardless of device state.
+ * The value is a boolean (1 or 0).
+ * @hide
+ */
+ private static final String FORCE_EMERGENCY_AFFORDANCE_SETTING = "force_emergency_affordance";
+
+ private final Context mContext;
+
+ public EmergencyAffordanceManager(Context context) {
+ mContext = context;
+ }
+
+ /**
+ * perform an emergency call.
+ */
+ public final void performEmergencyCall() {
+ performEmergencyCall(mContext);
+ }
+
+ private static Uri getPhoneUri(Context context) {
+ String number = context.getResources().getString(
+ com.android.internal.R.string.config_emergency_call_number);
+ if (Build.IS_DEBUGGABLE) {
+ String override = Settings.Global.getString(
+ context.getContentResolver(), EMERGENCY_CALL_NUMBER_SETTING);
+ if (override != null) {
+ number = override;
+ }
+ }
+ return Uri.fromParts("tel", number, null);
+ }
+
+ private static void performEmergencyCall(Context context) {
+ Intent intent = new Intent(Intent.ACTION_CALL_EMERGENCY);
+ intent.setData(getPhoneUri(context));
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ context.startActivity(intent);
+ }
+
+ /**
+ * @return whether emergency affordance should be active.
+ */
+ public boolean needsEmergencyAffordance() {
+ if (!ENABLED) {
+ return false;
+ }
+ if (forceShowing()) {
+ return true;
+ }
+ return isEmergencyAffordanceNeeded();
+ }
+
+ private boolean isEmergencyAffordanceNeeded() {
+ return Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.EMERGENCY_AFFORDANCE_NEEDED, 0) != 0;
+ }
+
+
+ private boolean forceShowing() {
+ return Settings.Global.getInt(mContext.getContentResolver(),
+ FORCE_EMERGENCY_AFFORDANCE_SETTING, 0) != 0;
+ }
+}
diff --git a/core/res/res/drawable/emergency_icon.xml b/core/res/res/drawable/emergency_icon.xml
new file mode 100644
index 000000000000..b2ffa2b4481c
--- /dev/null
+++ b/core/res/res/drawable/emergency_icon.xml
@@ -0,0 +1,40 @@
+<!--
+Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M6.8,17.3C5.3,15.9 4.5,14.0 4.5,12.0c0.0,-2.0 0.8,-3.8 2.1,-5.2l1.4,1.4c-1.0,1.0 -1.6,2.4 -1.6,3.8c0.0,1.5 0.6,2.9 1.6,3.9L6.8,17.3z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M3.3,20.2C1.2,18.0 0.0,15.1 0.0,12.0c0.0,-3.1 1.2,-6.0 3.3,-8.2l1.4,1.4C3.0,7.0 2.0,9.4 2.0,12.0s1.0,5.0 2.7,6.9L3.3,20.2z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17.2,17.3l-1.4,-1.4c1.1,-1.0 1.6,-2.4 1.6,-3.9c0.0,-1.4 -0.6,-2.8 -1.6,-3.8l1.4,-1.4c1.4,1.4 2.1,3.3 2.1,5.2C19.5,14.0 18.7,15.9 17.2,17.3z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20.7,20.2l-1.4,-1.4C21.0,17.0 22.0,14.6 22.0,12.0c0.0,-2.6 -1.0,-5.0 -2.7,-6.9l1.4,-1.4C22.8,6.0 24.0,8.9 24.0,12.0C24.0,15.1 22.8,18.0 20.7,20.2z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M11.0,15.0l2.0,0.0l0.0,2.0l-2.0,0.0z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M11.0,7.0l2.0,0.0l0.0,6.0l-2.0,0.0z"/>
+</vector>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 43f8dc769432..8a77fc94a48a 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2573,6 +2573,16 @@
<!-- True if the device supports system navigation keys. -->
<bool name="config_supportSystemNavigationKeys">false</bool>
+ <!-- emergency call number for the emergency affordance -->
+ <string name="config_emergency_call_number" translatable="false">112</string>
+
+ <!-- Do not translate. Mcc codes whose existence trigger the presence of emergency
+ affordances-->
+ <integer-array name="config_emergency_mcc_codes" translatable="false">
+ <item>404</item>
+ <item>405</item>
+ </integer-array>
+
<!-- Package name for the device provisioning package. -->
<string name="config_deviceProvisioningPackage"></string>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index bbcd6d57f30a..4080bb774a47 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -486,6 +486,9 @@
<!-- TODO: promote to separate string-->
<string name="global_action_restart" translatable="false">@string/sim_restart_button</string>
+ <!-- label for item that starts emergency call -->
+ <string name="global_action_emergency">Emergency</string>
+
<!-- label for item that generates a bug report in the phone options dialog -->
<string name="global_action_bug_report">Bug report</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index f02b9d0db646..7f093827204b 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2673,6 +2673,10 @@
<java-symbol type="string" name="lockscreen_storage_locked" />
+ <java-symbol type="string" name="global_action_emergency" />
+ <java-symbol type="string" name="config_emergency_call_number" />
+ <java-symbol type="array" name="config_emergency_mcc_codes" />
+
<!-- Used for MimeIconUtils. -->
<java-symbol type="drawable" name="ic_doc_apk" />
<java-symbol type="drawable" name="ic_doc_audio" />
@@ -2709,6 +2713,8 @@
<java-symbol type="drawable" name="ic_restart" />
+ <java-symbol type="drawable" name="emergency_icon" />
+
<java-symbol type="array" name="config_convert_to_emergency_number_map" />
<java-symbol type="array" name="config_nonBlockableNotificationPackages" />
diff --git a/packages/Keyguard/src/com/android/keyguard/EmergencyButton.java b/packages/Keyguard/src/com/android/keyguard/EmergencyButton.java
index 8d411453b5ae..0474df7bd89c 100644
--- a/packages/Keyguard/src/com/android/keyguard/EmergencyButton.java
+++ b/packages/Keyguard/src/com/android/keyguard/EmergencyButton.java
@@ -28,13 +28,16 @@ import android.os.UserHandle;
import android.telecom.TelecomManager;
import android.util.AttributeSet;
import android.util.Slog;
+import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewConfiguration;
import android.widget.Button;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.MetricsProto.MetricsEvent;
import com.android.internal.telephony.IccCardConstants.State;
import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.policy.EmergencyAffordanceManager;
/**
* This class implements a smart emergency button that updates itself based
@@ -51,7 +54,10 @@ public class EmergencyButton extends Button {
| Intent.FLAG_ACTIVITY_CLEAR_TOP);
private static final String LOG_TAG = "EmergencyButton";
+ private final EmergencyAffordanceManager mEmergencyAffordanceManager;
+ private int mDownX;
+ private int mDownY;
KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
@Override
@@ -64,6 +70,7 @@ public class EmergencyButton extends Button {
updateEmergencyCallButton();
}
};
+ private boolean mLongPressWasDragged;
public interface EmergencyButtonCallback {
public void onEmergencyButtonClickedWhenInCall();
@@ -86,6 +93,7 @@ public class EmergencyButton extends Button {
com.android.internal.R.bool.config_voice_capable);
mEnableEmergencyCallWhileSimLocked = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_enable_emergency_call_while_sim_locked);
+ mEmergencyAffordanceManager = new EmergencyAffordanceManager(context);
}
@Override
@@ -110,10 +118,45 @@ public class EmergencyButton extends Button {
takeEmergencyCallAction();
}
});
+ setOnLongClickListener(new OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View v) {
+ if (!mLongPressWasDragged
+ && mEmergencyAffordanceManager.needsEmergencyAffordance()) {
+ mEmergencyAffordanceManager.performEmergencyCall();
+ return true;
+ }
+ return false;
+ }
+ });
updateEmergencyCallButton();
}
@Override
+ public boolean onTouchEvent(MotionEvent event) {
+ final int x = (int) event.getX();
+ final int y = (int) event.getY();
+ if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ mDownX = x;
+ mDownY = y;
+ mLongPressWasDragged = false;
+ } else {
+ final int xDiff = Math.abs(x - mDownX);
+ final int yDiff = Math.abs(y - mDownY);
+ int touchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
+ if (Math.abs(yDiff) > touchSlop || Math.abs(xDiff) > touchSlop) {
+ mLongPressWasDragged = true;
+ }
+ }
+ return super.onTouchEvent(event);
+ }
+
+ @Override
+ public boolean performLongClick() {
+ return super.performLongClick();
+ }
+
+ @Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
updateEmergencyCallButton();
diff --git a/services/core/java/com/android/server/emergency/EmergencyAffordanceService.java b/services/core/java/com/android/server/emergency/EmergencyAffordanceService.java
new file mode 100644
index 000000000000..cca9f10c630b
--- /dev/null
+++ b/services/core/java/com/android/server/emergency/EmergencyAffordanceService.java
@@ -0,0 +1,312 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.emergency;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.provider.Settings;
+import android.telephony.CellInfo;
+import android.telephony.CellInfoGsm;
+import android.telephony.CellInfoLte;
+import android.telephony.CellInfoWcdma;
+import android.telephony.CellLocation;
+import android.telephony.PhoneStateListener;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+
+import com.android.server.SystemService;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * A service that listens to connectivity and SIM card changes and determines if the emergency mode
+ * should be enabled
+ */
+public class EmergencyAffordanceService extends SystemService {
+
+ private static final String TAG = "EmergencyAffordanceService";
+
+ private static final int NUM_SCANS_UNTIL_ABORT = 4;
+
+ private static final int INITIALIZE_STATE = 1;
+ private static final int CELL_INFO_STATE_CHANGED = 2;
+ private static final int SUBSCRIPTION_CHANGED = 3;
+
+ /**
+ * Global setting, whether the last scan of the sim cards reveal that a sim was inserted that
+ * requires the emergency affordance. The value is a boolean (1 or 0).
+ * @hide
+ */
+ private static final String EMERGENCY_SIM_INSERTED_SETTING = "emergency_sim_inserted_before";
+
+ private final Context mContext;
+ private final ArrayList<Integer> mEmergencyCallMccNumbers;
+
+ private final Object mLock = new Object();
+
+ private TelephonyManager mTelephonyManager;
+ private SubscriptionManager mSubscriptionManager;
+ private boolean mEmergencyAffordanceNeeded;
+ private MyHandler mHandler;
+ private int mScansCompleted;
+ private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
+ @Override
+ public void onCellInfoChanged(List<CellInfo> cellInfo) {
+ if (!isEmergencyAffordanceNeeded()) {
+ requestCellScan();
+ }
+ }
+
+ @Override
+ public void onCellLocationChanged(CellLocation location) {
+ if (!isEmergencyAffordanceNeeded()) {
+ requestCellScan();
+ }
+ }
+ };
+ private BroadcastReceiver mAirplaneModeReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.AIRPLANE_MODE_ON, 0) == 0) {
+ startScanning();
+ requestCellScan();
+ }
+ }
+ };
+ private boolean mSimNeedsEmergencyAffordance;
+ private boolean mNetworkNeedsEmergencyAffordance;
+
+ private void requestCellScan() {
+ mHandler.obtainMessage(CELL_INFO_STATE_CHANGED).sendToTarget();
+ }
+
+ private SubscriptionManager.OnSubscriptionsChangedListener mSubscriptionChangedListener
+ = new SubscriptionManager.OnSubscriptionsChangedListener() {
+ @Override
+ public void onSubscriptionsChanged() {
+ mHandler.obtainMessage(SUBSCRIPTION_CHANGED).sendToTarget();
+ }
+ };
+
+ public EmergencyAffordanceService(Context context) {
+ super(context);
+ mContext = context;
+ int[] numbers = context.getResources().getIntArray(
+ com.android.internal.R.array.config_emergency_mcc_codes);
+ mEmergencyCallMccNumbers = new ArrayList<>(numbers.length);
+ for (int i = 0; i < numbers.length; i++) {
+ mEmergencyCallMccNumbers.add(numbers[i]);
+ }
+ }
+
+ private void updateEmergencyAffordanceNeeded() {
+ synchronized (mLock) {
+ mEmergencyAffordanceNeeded = mSimNeedsEmergencyAffordance ||
+ mNetworkNeedsEmergencyAffordance;
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.EMERGENCY_AFFORDANCE_NEEDED,
+ mEmergencyAffordanceNeeded ? 1 : 0);
+ if (mEmergencyAffordanceNeeded) {
+ stopScanning();
+ }
+ }
+ }
+
+ private void stopScanning() {
+ synchronized (mLock) {
+ mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
+ mScansCompleted = 0;
+ }
+ }
+
+ private boolean isEmergencyAffordanceNeeded() {
+ synchronized (mLock) {
+ return mEmergencyAffordanceNeeded;
+ }
+ }
+
+ @Override
+ public void onStart() {
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
+ mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
+ mSubscriptionManager = SubscriptionManager.from(mContext);
+ HandlerThread thread = new HandlerThread(TAG);
+ thread.start();
+ mHandler = new MyHandler(thread.getLooper());
+ mHandler.obtainMessage(INITIALIZE_STATE).sendToTarget();
+ startScanning();
+ IntentFilter filter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+ mContext.registerReceiver(mAirplaneModeReceiver, filter);
+ mSubscriptionManager.addOnSubscriptionsChangedListener(mSubscriptionChangedListener);
+ }
+ }
+
+ private void startScanning() {
+ mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CELL_INFO
+ | PhoneStateListener.LISTEN_CELL_LOCATION);
+ }
+
+ /** Handler to do the heavier work on */
+ private class MyHandler extends Handler {
+
+ public MyHandler(Looper l) {
+ super(l);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case INITIALIZE_STATE:
+ handleInitializeState();
+ break;
+ case CELL_INFO_STATE_CHANGED:
+ handleUpdateCellInfo();
+ break;
+ case SUBSCRIPTION_CHANGED:
+ handleUpdateSimSubscriptionInfo();
+ break;
+ }
+ }
+ }
+
+ private void handleInitializeState() {
+ if (handleUpdateSimSubscriptionInfo()) {
+ return;
+ }
+ if (handleUpdateCellInfo()) {
+ return;
+ }
+ updateEmergencyAffordanceNeeded();
+ }
+
+ private boolean handleUpdateSimSubscriptionInfo() {
+ boolean neededBefore = simNeededAffordanceBefore();
+ boolean neededNow = neededBefore;
+ List<SubscriptionInfo> activeSubscriptionInfoList =
+ mSubscriptionManager.getActiveSubscriptionInfoList();
+ if (activeSubscriptionInfoList == null) {
+ return neededNow;
+ }
+ for (SubscriptionInfo info : activeSubscriptionInfoList) {
+ int mcc = info.getMcc();
+ if (mccRequiresEmergencyAffordance(mcc)) {
+ neededNow = true;
+ break;
+ } else if (mcc != 0 && mcc != Integer.MAX_VALUE){
+ // a Sim with a different mcc code was found
+ neededNow = false;
+ }
+ String simOperator = mTelephonyManager.getSimOperator(info.getSubscriptionId());
+ mcc = 0;
+ if (simOperator != null && simOperator.length() >= 3) {
+ mcc = Integer.parseInt(simOperator.substring(0, 3));
+ }
+ if (mcc != 0) {
+ if (mccRequiresEmergencyAffordance(mcc)) {
+ neededNow = true;
+ break;
+ } else {
+ // a Sim with a different mcc code was found
+ neededNow = false;
+ }
+ }
+ }
+ if (neededNow != neededBefore) {
+ setSimNeedsEmergencyAffordance(neededNow);
+ }
+ return neededNow;
+ }
+
+ private void setSimNeedsEmergencyAffordance(boolean simNeedsEmergencyAffordance) {
+ mSimNeedsEmergencyAffordance = simNeedsEmergencyAffordance;
+ Settings.Global.putInt(mContext.getContentResolver(),
+ EMERGENCY_SIM_INSERTED_SETTING,
+ simNeedsEmergencyAffordance ? 1 : 0);
+ updateEmergencyAffordanceNeeded();
+ }
+
+ private boolean simNeededAffordanceBefore() {
+ return Settings.Global.getInt(mContext.getContentResolver(),
+ "emergency_sim_inserted_before", 0) != 0;
+ }
+
+ private boolean handleUpdateCellInfo() {
+ List<CellInfo> cellInfos = mTelephonyManager.getAllCellInfo();
+ if (cellInfos == null) {
+ return false;
+ }
+ boolean stopScanningAfterScan = false;
+ for (CellInfo cellInfo : cellInfos) {
+ int mcc = 0;
+ if (cellInfo instanceof CellInfoGsm) {
+ mcc = ((CellInfoGsm) cellInfo).getCellIdentity().getMcc();
+ } else if (cellInfo instanceof CellInfoLte) {
+ mcc = ((CellInfoLte) cellInfo).getCellIdentity().getMcc();
+ } else if (cellInfo instanceof CellInfoWcdma) {
+ mcc = ((CellInfoWcdma) cellInfo).getCellIdentity().getMcc();
+ }
+ if (mccRequiresEmergencyAffordance(mcc)) {
+ setNetworkNeedsEmergencyAffordance(true);
+ return true;
+ } else if (mcc != 0 && mcc != Integer.MAX_VALUE) {
+ // we found an mcc that isn't in the list, abort
+ stopScanningAfterScan = true;
+ }
+ }
+ if (stopScanningAfterScan) {
+ stopScanning();
+ } else {
+ onCellScanFinishedUnsuccessful();
+ }
+ setNetworkNeedsEmergencyAffordance(false);
+ return false;
+ }
+
+ private void setNetworkNeedsEmergencyAffordance(boolean needsAffordance) {
+ synchronized (mLock) {
+ mNetworkNeedsEmergencyAffordance = needsAffordance;
+ updateEmergencyAffordanceNeeded();
+ }
+ }
+
+ private void onCellScanFinishedUnsuccessful() {
+ synchronized (mLock) {
+ mScansCompleted++;
+ if (mScansCompleted >= NUM_SCANS_UNTIL_ABORT) {
+ stopScanning();
+ }
+ }
+ }
+
+ private boolean mccRequiresEmergencyAffordance(int mcc) {
+ return mEmergencyCallMccNumbers.contains(mcc);
+ }
+}
diff --git a/services/core/java/com/android/server/policy/GlobalActions.java b/services/core/java/com/android/server/policy/GlobalActions.java
index bb91f76ecf30..a8bd4d289616 100644
--- a/services/core/java/com/android/server/policy/GlobalActions.java
+++ b/services/core/java/com/android/server/policy/GlobalActions.java
@@ -20,6 +20,7 @@ import com.android.internal.app.AlertController;
import com.android.internal.app.AlertController.AlertParams;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.policy.EmergencyAffordanceManager;
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.telephony.TelephonyProperties;
import com.android.internal.R;
@@ -27,7 +28,6 @@ import com.android.internal.widget.LockPatternUtils;
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
-import android.app.AlertDialog;
import android.app.Dialog;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -125,6 +125,7 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
private boolean mHasTelephony;
private boolean mHasVibrator;
private final boolean mShowSilentToggle;
+ private final EmergencyAffordanceManager mEmergencyAffordanceManager;
/**
* @param context everything needs a context :(
@@ -159,6 +160,8 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
mShowSilentToggle = SHOW_SILENT_TOGGLE && !mContext.getResources().getBoolean(
com.android.internal.R.bool.config_useFixedVolume);
+
+ mEmergencyAffordanceManager = new EmergencyAffordanceManager(context);
}
/**
@@ -308,6 +311,10 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
addedKeys.add(actionKey);
}
+ if (mEmergencyAffordanceManager.needsEmergencyAffordance()) {
+ mItems.add(getEmergencyAction());
+ }
+
mAdapter = new MyAdapter();
AlertParams params = new AlertParams(mContext);
@@ -493,6 +500,26 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
};
}
+ private Action getEmergencyAction() {
+ return new SinglePressAction(com.android.internal.R.drawable.emergency_icon,
+ R.string.global_action_emergency) {
+ @Override
+ public void onPress() {
+ mEmergencyAffordanceManager.performEmergencyCall();
+ }
+
+ @Override
+ public boolean showDuringKeyguard() {
+ return true;
+ }
+
+ @Override
+ public boolean showBeforeProvisioning() {
+ return true;
+ }
+ };
+ }
+
private Action getAssistAction() {
return new SinglePressAction(com.android.internal.R.drawable.ic_action_assist_focused,
R.string.global_action_assist) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 555d9b1e3f20..0de6b7790856 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -16,7 +16,6 @@
package com.android.server;
-import android.app.ActivityManagerNative;
import android.app.ActivityThread;
import android.app.INotificationManager;
import android.app.usage.UsageStatsManagerInternal;
@@ -32,7 +31,6 @@ import android.os.Build;
import android.os.Environment;
import android.os.FactoryTest;
import android.os.FileUtils;
-import android.os.IPowerManager;
import android.os.Looper;
import android.os.PowerManager;
import android.os.RemoteException;
@@ -55,6 +53,7 @@ import com.android.internal.app.NightDisplayController;
import com.android.internal.os.BinderInternal;
import com.android.internal.os.SamplingProfilerIntegration;
import com.android.internal.os.ZygoteInit;
+import com.android.internal.policy.EmergencyAffordanceManager;
import com.android.internal.widget.ILockSettings;
import com.android.server.accessibility.AccessibilityManagerService;
import com.android.server.am.ActivityManagerService;
@@ -67,6 +66,7 @@ import com.android.server.devicepolicy.DevicePolicyManagerService;
import com.android.server.display.DisplayManagerService;
import com.android.server.display.NightDisplayService;
import com.android.server.dreams.DreamManagerService;
+import com.android.server.emergency.EmergencyAffordanceService;
import com.android.server.fingerprint.FingerprintService;
import com.android.server.hdmi.HdmiControlService;
import com.android.server.input.InputManagerService;
@@ -1097,6 +1097,11 @@ public final class SystemServer {
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
+ if (!disableNetwork && !disableNonCoreServices && EmergencyAffordanceManager.ENABLED) {
+ // EmergencyMode sevice
+ mSystemServiceManager.startService(EmergencyAffordanceService.class);
+ }
+
if (!disableNonCoreServices) {
// Dreams (interactive idle-time views, a/k/a screen savers, and doze mode)
mSystemServiceManager.startService(DreamManagerService.class);