Merge "DO NOT MERGE Institute limit on PhoneStateListener" into qt-qpr1-dev
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 31bbd16..3b620b3 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -1195,9 +1195,11 @@
     public boolean factoryReset() {
         try {
             mServiceLock.readLock().lock();
-            if (mService != null) {
-                return mService.factoryReset();
+            if (mService != null && mService.factoryReset()
+                    && mManagerService != null && mManagerService.onFactoryReset()) {
+                return true;
             }
+            Log.e(TAG, "factoryReset(): Setting persist.bluetooth.factoryreset to retry later");
             SystemProperties.set("persist.bluetooth.factoryreset", "true");
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
diff --git a/core/proto/android/bluetooth/enums.proto b/core/proto/android/bluetooth/enums.proto
index b4f3d1e..22f2498 100644
--- a/core/proto/android/bluetooth/enums.proto
+++ b/core/proto/android/bluetooth/enums.proto
@@ -40,6 +40,7 @@
     ENABLE_DISABLE_REASON_CRASH = 7;
     ENABLE_DISABLE_REASON_USER_SWITCH = 8;
     ENABLE_DISABLE_REASON_RESTORE_USER_SETTING = 9;
+    ENABLE_DISABLE_REASON_FACTORY_RESET = 10;
 }
 
 enum DirectionEnum {
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 188d654..f88032a 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -73,11 +73,14 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.Arrays;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.Locale;
 import java.util.Map;
 import java.util.NoSuchElementException;
+import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 
@@ -257,6 +260,47 @@
                 }
             };
 
+    public boolean onFactoryReset() {
+        // Wait for stable state if bluetooth is temporary state.
+        int state = getState();
+        if (state == BluetoothAdapter.STATE_BLE_TURNING_ON
+                || state == BluetoothAdapter.STATE_TURNING_ON
+                || state == BluetoothAdapter.STATE_TURNING_OFF) {
+            if (!waitForState(new HashSet<Integer>(Arrays.asList(BluetoothAdapter.STATE_BLE_ON,
+                      BluetoothAdapter.STATE_ON)))) {
+                return false;
+            }
+        }
+
+        // Clear registered LE apps to force shut-off Bluetooth
+        clearBleApps();
+        state = getState();
+        try {
+            mBluetoothLock.readLock().lock();
+            if (mBluetooth == null) {
+                return false;
+            }
+            if (state == BluetoothAdapter.STATE_BLE_ON) {
+                addActiveLog(
+                        BluetoothProtoEnums.ENABLE_DISABLE_REASON_FACTORY_RESET,
+                        mContext.getPackageName(), false);
+                mBluetooth.onBrEdrDown();
+                return true;
+            } else if (state == BluetoothAdapter.STATE_ON) {
+                addActiveLog(
+                        BluetoothProtoEnums.ENABLE_DISABLE_REASON_FACTORY_RESET,
+                        mContext.getPackageName(), false);
+                mBluetooth.disable();
+                return true;
+            }
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Unable to shutdown Bluetooth", e);
+        } finally {
+            mBluetoothLock.readLock().unlock();
+        }
+        return false;
+    }
+
     private final ContentObserver mAirplaneModeObserver = new ContentObserver(null) {
         @Override
         public void onChange(boolean unused) {
@@ -1627,7 +1671,9 @@
                         // the previous Bluetooth process has exited. The
                         // waiting period has three components:
                         // (a) Wait until the local state is STATE_OFF. This
-                        //     is accomplished by "waitForOnOff(false, true)".
+                        //     is accomplished by
+                        //     "waitForState(new HashSet<Integer>(
+                        //     Arrays.asList(BluetoothAdapter.STATE_OFF)))".
                         // (b) Wait until the STATE_OFF state is updated to
                         //     all components.
                         // (c) Wait until the Bluetooth process exits, and
@@ -1637,7 +1683,8 @@
                         // message. On slower devices, that delay needs to be
                         // on the order of (2 * SERVICE_RESTART_TIME_MS).
                         //
-                        waitForOnOff(false, true);
+                        waitForState(new HashSet<Integer>(Arrays.asList(
+                                BluetoothAdapter.STATE_OFF)));
                         Message restartMsg =
                                 mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE);
                         mHandler.sendMessageDelayed(restartMsg, 2 * SERVICE_RESTART_TIME_MS);
@@ -1650,10 +1697,16 @@
                     }
                     mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE);
                     if (mEnable && mBluetooth != null) {
-                        waitForOnOff(true, false);
+                        waitForState(new HashSet<Integer>(Arrays.asList(
+                                BluetoothAdapter.STATE_ON)));
                         mEnable = false;
                         handleDisable();
-                        waitForOnOff(false, false);
+                        waitForState(new HashSet<Integer>(Arrays.asList(BluetoothAdapter.STATE_OFF,
+                                BluetoothAdapter.STATE_TURNING_ON,
+                                BluetoothAdapter.STATE_TURNING_OFF,
+                                BluetoothAdapter.STATE_BLE_TURNING_ON,
+                                BluetoothAdapter.STATE_BLE_ON,
+                                BluetoothAdapter.STATE_BLE_TURNING_OFF)));
                     } else {
                         mEnable = false;
                         handleDisable();
@@ -1782,9 +1835,15 @@
                     }
 
                     if (!mEnable) {
-                        waitForOnOff(true, false);
+                        waitForState(new HashSet<Integer>(Arrays.asList(
+                                BluetoothAdapter.STATE_ON)));
                         handleDisable();
-                        waitForOnOff(false, false);
+                        waitForState(new HashSet<Integer>(Arrays.asList(BluetoothAdapter.STATE_OFF,
+                                BluetoothAdapter.STATE_TURNING_ON,
+                                BluetoothAdapter.STATE_TURNING_OFF,
+                                BluetoothAdapter.STATE_BLE_TURNING_ON,
+                                BluetoothAdapter.STATE_BLE_ON,
+                                BluetoothAdapter.STATE_BLE_TURNING_OFF)));
                     }
                     break;
                 }
@@ -1816,7 +1875,8 @@
                             == BluetoothAdapter.STATE_OFF)) {
                         if (mEnable) {
                             Slog.d(TAG, "Entering STATE_OFF but mEnabled is true; restarting.");
-                            waitForOnOff(false, true);
+                            waitForState(new HashSet<Integer>(Arrays.asList(
+                                    BluetoothAdapter.STATE_OFF)));
                             Message restartMsg =
                                     mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE);
                             mHandler.sendMessageDelayed(restartMsg, 2 * SERVICE_RESTART_TIME_MS);
@@ -1939,7 +1999,8 @@
                             mState = BluetoothAdapter.STATE_TURNING_ON;
                         }
 
-                        waitForOnOff(true, false);
+                        waitForState(new HashSet<Integer>(Arrays.asList(
+                                BluetoothAdapter.STATE_ON)));
 
                         if (mState == BluetoothAdapter.STATE_TURNING_ON) {
                             bluetoothStateChangeHandler(mState, BluetoothAdapter.STATE_ON);
@@ -1954,7 +2015,9 @@
                         bluetoothStateChangeHandler(BluetoothAdapter.STATE_ON,
                                 BluetoothAdapter.STATE_TURNING_OFF);
 
-                        boolean didDisableTimeout = !waitForOnOff(false, true);
+                        boolean didDisableTimeout =
+                                !waitForState(new HashSet<Integer>(Arrays.asList(
+                                    BluetoothAdapter.STATE_OFF)));
 
                         bluetoothStateChangeHandler(BluetoothAdapter.STATE_TURNING_OFF,
                                 BluetoothAdapter.STATE_OFF);
@@ -2206,12 +2269,7 @@
         }
     }
 
-    /**
-     *  if on is true, wait for state become ON
-     *  if off is true, wait for state become OFF
-     *  if both on and off are false, wait for state not ON
-     */
-    private boolean waitForOnOff(boolean on, boolean off) {
+    private boolean waitForState(Set<Integer> states) {
         int i = 0;
         while (i < 10) {
             try {
@@ -2219,18 +2277,8 @@
                 if (mBluetooth == null) {
                     break;
                 }
-                if (on) {
-                    if (mBluetooth.getState() == BluetoothAdapter.STATE_ON) {
-                        return true;
-                    }
-                } else if (off) {
-                    if (mBluetooth.getState() == BluetoothAdapter.STATE_OFF) {
-                        return true;
-                    }
-                } else {
-                    if (mBluetooth.getState() != BluetoothAdapter.STATE_ON) {
-                        return true;
-                    }
+                if (states.contains(mBluetooth.getState())) {
+                    return true;
                 }
             } catch (RemoteException e) {
                 Slog.e(TAG, "getState()", e);
@@ -2238,14 +2286,10 @@
             } finally {
                 mBluetoothLock.readLock().unlock();
             }
-            if (on || off) {
-                SystemClock.sleep(300);
-            } else {
-                SystemClock.sleep(50);
-            }
+            SystemClock.sleep(300);
             i++;
         }
-        Slog.e(TAG, "waitForOnOff time out");
+        Slog.e(TAG, "waitForState " + states + " time out");
         return false;
     }
 
@@ -2306,7 +2350,7 @@
                 mContext.getPackageName(), false);
         handleDisable();
 
-        waitForOnOff(false, true);
+        waitForState(new HashSet<Integer>(Arrays.asList(BluetoothAdapter.STATE_OFF)));
 
         sendBluetoothServiceDownCallback();
 
@@ -2468,6 +2512,8 @@
                 return "USER_SWITCH";
             case BluetoothProtoEnums.ENABLE_DISABLE_REASON_RESTORE_USER_SETTING:
                 return "RESTORE_USER_SETTING";
+            case BluetoothProtoEnums.ENABLE_DISABLE_REASON_FACTORY_RESET:
+                return "FACTORY_RESET";
             case BluetoothProtoEnums.ENABLE_DISABLE_REASON_UNSPECIFIED:
             default: return "UNKNOWN[" + reason + "]";
         }
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 2e4fe55..f9a955e 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -2426,7 +2426,7 @@
      *            most important plan. Any additional plans are secondary and
      *            may not be displayed or used by decision making logic.
      *            The list of all plans must meet the requirements defined in
-     *            {@link SubscriptionPlan.Builder#setNetworkTypes(int[])}.
+     *            SubscriptionPlan.Builder#setNetworkTypes(int[]).
      * @throws SecurityException if the caller doesn't meet the requirements
      *             outlined above.
      * @throws IllegalArgumentException if plans don't meet the requirements