AudioService/WireAccessoryManager: change boot completion detection method

BOOT_COMPLETED intent is not a reliable way for system services
to detect boot completion. The intent broadcast can be significantly
delayed and there is no guaranty that system services
receive it before apps.

Use a systemReady() method called by SystemServer instead.

Bug: 14323903.
Change-Id: I781596a3545e7a1e719799982347cbcd9a4c9009
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 0c8a823..724022b 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -149,6 +149,7 @@
     private static final int MSG_PERSIST_SAFE_VOLUME_STATE = 18;
     private static final int MSG_BROADCAST_BT_CONNECTION_STATE = 19;
     private static final int MSG_UNLOAD_SOUND_EFFECTS = 20;
+    private static final int MSG_SYSTEM_READY = 21;
     // start of messages handled under wakelock
     //   these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
     //   and not with sendMsg(..., ..., SENDMSG_QUEUE, ...)
@@ -370,7 +371,7 @@
     private int mScoConnectionState;
 
     // true if boot sequence has been completed
-    private boolean mBootCompleted;
+    private boolean mSystemReady;
     // listener for SoundPool sample load completion indication
     private SoundPoolCallback mSoundPoolCallBack;
     // thread for SoundPool listener
@@ -525,7 +526,6 @@
         intentFilter.addAction(Intent.ACTION_DOCK_EVENT);
         intentFilter.addAction(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG);
         intentFilter.addAction(Intent.ACTION_USB_AUDIO_DEVICE_PLUG);
-        intentFilter.addAction(Intent.ACTION_BOOT_COMPLETED);
         intentFilter.addAction(Intent.ACTION_SCREEN_ON);
         intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
         intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
@@ -559,6 +559,43 @@
 
     }
 
+    public void systemReady() {
+        sendMsg(mAudioHandler, MSG_SYSTEM_READY, SENDMSG_QUEUE,
+                0, 0, null, 0);
+    }
+
+    public void onSystemReady() {
+        mSystemReady = true;
+        sendMsg(mAudioHandler, MSG_LOAD_SOUND_EFFECTS, SENDMSG_QUEUE,
+                0, 0, null, 0);
+
+        mKeyguardManager =
+                (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
+        mScoConnectionState = AudioManager.SCO_AUDIO_STATE_ERROR;
+        resetBluetoothSco();
+        getBluetoothHeadset();
+        //FIXME: this is to maintain compatibility with deprecated intent
+        // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate.
+        Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
+        newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE,
+                AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+        sendStickyBroadcastToAll(newIntent);
+
+        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+        if (adapter != null) {
+            adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener,
+                                    BluetoothProfile.A2DP);
+        }
+
+        sendMsg(mAudioHandler,
+                MSG_CONFIGURE_SAFE_MEDIA_VOLUME_FORCED,
+                SENDMSG_REPLACE,
+                0,
+                0,
+                null,
+                SAFE_VOLUME_CONFIGURE_TIMEOUT_MS);
+    }
+
     private void createAudioSystemThread() {
         mAudioSystemThread = new AudioSystemThread();
         mAudioSystemThread.start();
@@ -1996,7 +2033,7 @@
     /** @see AudioManager#startBluetoothSco() */
     public void startBluetoothSco(IBinder cb, int targetSdkVersion){
         if (!checkAudioSettingsPermission("startBluetoothSco()") ||
-                !mBootCompleted) {
+                !mSystemReady) {
             return;
         }
         ScoClient client = getScoClient(cb, true);
@@ -2013,7 +2050,7 @@
     /** @see AudioManager#stopBluetoothSco() */
     public void stopBluetoothSco(IBinder cb){
         if (!checkAudioSettingsPermission("stopBluetoothSco()") ||
-                !mBootCompleted) {
+                !mSystemReady) {
             return;
         }
         ScoClient client = getScoClient(cb, false);
@@ -3277,7 +3314,7 @@
             int status;
 
             synchronized (mSoundEffectsLock) {
-                if (!mBootCompleted) {
+                if (!mSystemReady) {
                     Log.w(TAG, "onLoadSoundEffects() called before boot complete");
                     return false;
                 }
@@ -3700,6 +3737,10 @@
                 case MSG_BROADCAST_BT_CONNECTION_STATE:
                     onBroadcastScoConnectionState(msg.arg1);
                     break;
+
+                case MSG_SYSTEM_READY:
+                    onSystemReady();
+                    break;
             }
         }
     }
@@ -4169,36 +4210,6 @@
                     newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, scoAudioState);
                     sendStickyBroadcastToAll(newIntent);
                 }
-            } else if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
-                mBootCompleted = true;
-                sendMsg(mAudioHandler, MSG_LOAD_SOUND_EFFECTS, SENDMSG_QUEUE,
-                        0, 0, null, 0);
-
-                mKeyguardManager =
-                        (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
-                mScoConnectionState = AudioManager.SCO_AUDIO_STATE_ERROR;
-                resetBluetoothSco();
-                getBluetoothHeadset();
-                //FIXME: this is to maintain compatibility with deprecated intent
-                // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate.
-                Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
-                newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE,
-                        AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
-                sendStickyBroadcastToAll(newIntent);
-
-                BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
-                if (adapter != null) {
-                    adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener,
-                                            BluetoothProfile.A2DP);
-                }
-
-                sendMsg(mAudioHandler,
-                        MSG_CONFIGURE_SAFE_MEDIA_VOLUME_FORCED,
-                        SENDMSG_REPLACE,
-                        0,
-                        0,
-                        null,
-                        SAFE_VOLUME_CONFIGURE_TIMEOUT_MS);
             } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
                 AudioSystem.setParameters("screen_state=on");
             } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
diff --git a/services/core/java/com/android/server/WiredAccessoryManager.java b/services/core/java/com/android/server/WiredAccessoryManager.java
index 50cfe48..c32beda 100644
--- a/services/core/java/com/android/server/WiredAccessoryManager.java
+++ b/services/core/java/com/android/server/WiredAccessoryManager.java
@@ -70,6 +70,7 @@
     private static final String NAME_HDMI = "hdmi";
 
     private static final int MSG_NEW_DEVICE_STATE = 1;
+    private static final int MSG_SYSTEM_READY = 2;
 
     private final Object mLock = new Object();
 
@@ -96,19 +97,9 @@
                 context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);
 
         mObserver = new WiredAccessoryObserver();
-
-        IntentFilter filter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
-        filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
-        context.registerReceiver(new BroadcastReceiver() {
-                    @Override
-                    public void onReceive(Context ctx, Intent intent) {
-                        bootCompleted();
-                    }
-                },
-                filter, null, null);
     }
 
-    private void bootCompleted() {
+    private void onSystemReady() {
         if (mUseDevInputEventForAudioJack) {
             int switchValues = 0;
             if (mInputManager.getSwitchState(-1, InputDevice.SOURCE_ANY, SW_HEADPHONE_INSERT) == 1) {
@@ -159,6 +150,16 @@
         }
     }
 
+    @Override
+    public void systemReady() {
+        synchronized (mLock) {
+            mWakeLock.acquire();
+
+            Message msg = mHandler.obtainMessage(MSG_SYSTEM_READY, 0, 0, null);
+            mHandler.sendMessage(msg);
+        }
+    }
+
     /**
      * Compare the existing headset state with the new state and pass along accordingly. Note
      * that this only supports a single headset at a time. Inserting both a usb and jacked headset
@@ -220,6 +221,11 @@
                 case MSG_NEW_DEVICE_STATE:
                     setDevicesState(msg.arg1, msg.arg2, (String)msg.obj);
                     mWakeLock.release();
+                    break;
+                case MSG_SYSTEM_READY:
+                    onSystemReady();
+                    mWakeLock.release();
+                    break;
             }
         }
     };
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 54cb035..0f5805c 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -323,6 +323,10 @@
 
         mHandler.sendEmptyMessage(MSG_RELOAD_DEVICE_ALIASES);
         mHandler.sendEmptyMessage(MSG_UPDATE_KEYBOARD_LAYOUTS);
+
+        if (mWiredAccessoryCallbacks != null) {
+            mWiredAccessoryCallbacks.systemReady();
+        }
     }
 
     private void reloadKeyboardLayouts() {
@@ -1588,6 +1592,7 @@
      */
     public interface WiredAccessoryCallbacks {
         public void notifyWiredAccessoryChanged(long whenNanos, int switchValues, int switchMask);
+        public void systemReady();
     }
 
     /**
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 7c9f7a8..58c43fb 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -333,6 +333,7 @@
         InputManagerService inputManager = null;
         TelephonyRegistry telephonyRegistry = null;
         ConsumerIrService consumerIr = null;
+        AudioService audioService = null;
 
         boolean onlyCore = false;
         boolean firstBoot = false;
@@ -759,7 +760,8 @@
             if (!disableMedia && !"0".equals(SystemProperties.get("system_init.startaudioservice"))) {
                 try {
                     Slog.i(TAG, "Audio Service");
-                    ServiceManager.addService(Context.AUDIO_SERVICE, new AudioService(context));
+                    audioService = new AudioService(context);
+                    ServiceManager.addService(Context.AUDIO_SERVICE, audioService);
                 } catch (Throwable e) {
                     reportWtf("starting Audio Service", e);
                 }
@@ -1067,6 +1069,7 @@
         final InputManagerService inputManagerF = inputManager;
         final TelephonyRegistry telephonyRegistryF = telephonyRegistry;
         final MediaRouterService mediaRouterF = mediaRouter;
+        final AudioService audioServiceF = audioService;
 
         // We now tell the activity manager it is okay to run third party
         // code.  It will call back into us once it has gotten to the state
@@ -1135,6 +1138,11 @@
                 } catch (Throwable e) {
                     reportWtf("making Recognition Service ready", e);
                 }
+                try {
+                    if (audioServiceF != null) audioServiceF.systemReady();
+                } catch (Throwable e) {
+                    reportWtf("Notifying AudioService running", e);
+                }
                 Watchdog.getInstance().start();
 
                 // It is now okay to let the various system services start their