Merge "Track disabled car mode ICS in InCallController when UI mode changed." into sc-dev
diff --git a/scripts/aosp_tag_preupload.py b/scripts/aosp_tag_preupload.py
index 17f5058..bfcdbd6 100755
--- a/scripts/aosp_tag_preupload.py
+++ b/scripts/aosp_tag_preupload.py
@@ -41,11 +41,14 @@
   commit_msg = subprocess.check_output(["git", "show",
                                         sys.argv[1], "--no-notes"])
   for commit_line in commit_msg.splitlines():
-    if re.search(AOSP_COMMIT_TAG_REGEX, commit_line, re.IGNORECASE):
-      _check_aosp_message(commit_line)
+    # Some lines in the commit message will be given to us as bytes
+    commit_line_str = str(commit_line)
+    if re.search(AOSP_COMMIT_TAG_REGEX, str(commit_line_str), re.IGNORECASE):
+      _check_aosp_message(commit_line_str)
 
   print(ERROR_MESSAGE)
-  sys.exit(0)
+  # Print the warning, but do not fail the presubmit check.
+  sys.exit(77)
 
 def _is_in_aosp():
   branch_info = subprocess.check_output(["git", "branch", "-vv"])
@@ -59,7 +62,8 @@
     sys.exit(0)
 
   print(ERROR_MESSAGE)
-  sys.exit(0)
+  # Print the warning, but do not fail the presubmit check.
+  sys.exit(77)
 
 if __name__ == '__main__':
   main()
diff --git a/src/com/android/server/telecom/InCallTonePlayer.java b/src/com/android/server/telecom/InCallTonePlayer.java
index 7500436..524d119 100644
--- a/src/com/android/server/telecom/InCallTonePlayer.java
+++ b/src/com/android/server/telecom/InCallTonePlayer.java
@@ -439,7 +439,7 @@
                 @Override
                 public void onCompletion(MediaPlayer mp) {
                     Log.i(this, "playMediaTone: toneResourceId=%d completed.", toneResourceId);
-                    synchronized (this) {
+                    synchronized (InCallTonePlayer.this) {
                         mState = STATE_OFF;
                     }
                     mToneMediaPlayer.release();
diff --git a/src/com/android/server/telecom/TelecomSystem.java b/src/com/android/server/telecom/TelecomSystem.java
index 613f50d..c824a65 100644
--- a/src/com/android/server/telecom/TelecomSystem.java
+++ b/src/com/android/server/telecom/TelecomSystem.java
@@ -217,199 +217,208 @@
                 defaultDialerAdapter, roleManagerAdapter, mLock);
 
         Log.startSession("TS.init");
-        mPhoneAccountRegistrar = new PhoneAccountRegistrar(mContext, defaultDialerCache,
-                packageName -> AppLabelProxy.Util.getAppLabel(
-                        mContext.getPackageManager(), packageName));
+        // Wrap this in a try block to ensure session cleanup occurs in the case of error.
+        try {
+            mPhoneAccountRegistrar = new PhoneAccountRegistrar(mContext, defaultDialerCache,
+                    packageName -> AppLabelProxy.Util.getAppLabel(
+                            mContext.getPackageManager(), packageName));
 
-        mContactsAsyncHelper = contactsAsyncHelperFactory.create(
-                new ContactsAsyncHelper.ContentResolverAdapter() {
-                    @Override
-                    public InputStream openInputStream(Context context, Uri uri)
-                            throws FileNotFoundException {
-                        return context.getContentResolver().openInputStream(uri);
-                    }
-                });
-        BluetoothDeviceManager bluetoothDeviceManager = new BluetoothDeviceManager(mContext,
-                new BluetoothAdapterProxy());
-        BluetoothRouteManager bluetoothRouteManager = new BluetoothRouteManager(mContext, mLock,
-                bluetoothDeviceManager, new Timeouts.Adapter());
-        BluetoothStateReceiver bluetoothStateReceiver = new BluetoothStateReceiver(
-                bluetoothDeviceManager, bluetoothRouteManager);
-        mContext.registerReceiver(bluetoothStateReceiver, BluetoothStateReceiver.INTENT_FILTER);
+            mContactsAsyncHelper = contactsAsyncHelperFactory.create(
+                    new ContactsAsyncHelper.ContentResolverAdapter() {
+                        @Override
+                        public InputStream openInputStream(Context context, Uri uri)
+                                throws FileNotFoundException {
+                            return context.getContentResolver().openInputStream(uri);
+                        }
+                    });
+            BluetoothDeviceManager bluetoothDeviceManager = new BluetoothDeviceManager(mContext,
+                    new BluetoothAdapterProxy());
+            BluetoothRouteManager bluetoothRouteManager = new BluetoothRouteManager(mContext, mLock,
+                    bluetoothDeviceManager, new Timeouts.Adapter());
+            BluetoothStateReceiver bluetoothStateReceiver = new BluetoothStateReceiver(
+                    bluetoothDeviceManager, bluetoothRouteManager);
+            mContext.registerReceiver(bluetoothStateReceiver, BluetoothStateReceiver.INTENT_FILTER);
 
-        WiredHeadsetManager wiredHeadsetManager = new WiredHeadsetManager(mContext);
-        SystemStateHelper systemStateHelper = new SystemStateHelper(mContext, mLock);
+            WiredHeadsetManager wiredHeadsetManager = new WiredHeadsetManager(mContext);
+            SystemStateHelper systemStateHelper = new SystemStateHelper(mContext, mLock);
 
-        mMissedCallNotifier = missedCallNotifierImplFactory
-                .makeMissedCallNotifierImpl(mContext, mPhoneAccountRegistrar, defaultDialerCache,
-                        deviceIdleControllerAdapter);
-        DisconnectedCallNotifier.Factory disconnectedCallNotifierFactory =
-                new DisconnectedCallNotifier.Default();
+            mMissedCallNotifier = missedCallNotifierImplFactory
+                    .makeMissedCallNotifierImpl(mContext, mPhoneAccountRegistrar,
+                            defaultDialerCache,
+                            deviceIdleControllerAdapter);
+            DisconnectedCallNotifier.Factory disconnectedCallNotifierFactory =
+                    new DisconnectedCallNotifier.Default();
 
-        CallerInfoLookupHelper callerInfoLookupHelper =
-                new CallerInfoLookupHelper(context, callerInfoAsyncQueryFactory,
-                        mContactsAsyncHelper, mLock);
+            CallerInfoLookupHelper callerInfoLookupHelper =
+                    new CallerInfoLookupHelper(context, callerInfoAsyncQueryFactory,
+                            mContactsAsyncHelper, mLock);
 
-        EmergencyCallHelper emergencyCallHelper = new EmergencyCallHelper(mContext,
-                defaultDialerCache, timeoutsAdapter);
+            EmergencyCallHelper emergencyCallHelper = new EmergencyCallHelper(mContext,
+                    defaultDialerCache, timeoutsAdapter);
 
-        InCallControllerFactory inCallControllerFactory = new InCallControllerFactory() {
-            @Override
-            public InCallController create(Context context, SyncRoot lock,
-                    CallsManager callsManager, SystemStateHelper systemStateProvider,
-                    DefaultDialerCache defaultDialerCache, Timeouts.Adapter timeoutsAdapter,
-                    EmergencyCallHelper emergencyCallHelper) {
-                return new InCallController(context, lock, callsManager, systemStateProvider,
-                        defaultDialerCache, timeoutsAdapter, emergencyCallHelper,
-                        new CarModeTracker(), clockProxy);
-            }
-        };
+            InCallControllerFactory inCallControllerFactory = new InCallControllerFactory() {
+                @Override
+                public InCallController create(Context context, SyncRoot lock,
+                        CallsManager callsManager, SystemStateHelper systemStateProvider,
+                        DefaultDialerCache defaultDialerCache, Timeouts.Adapter timeoutsAdapter,
+                        EmergencyCallHelper emergencyCallHelper) {
+                    return new InCallController(context, lock, callsManager, systemStateProvider,
+                            defaultDialerCache, timeoutsAdapter, emergencyCallHelper,
+                            new CarModeTracker(), clockProxy);
+                }
+            };
 
-        CallDiagnosticServiceController callDiagnosticServiceController =
-                new CallDiagnosticServiceController(
-                        new CallDiagnosticServiceController.ContextProxy() {
-                            @Override
-                            public List<ResolveInfo> queryIntentServicesAsUser(
-                                    @NonNull Intent intent, int flags, int userId) {
-                                return mContext.getPackageManager().queryIntentServicesAsUser(
-                                        intent, flags, userId);
-                            }
+            CallDiagnosticServiceController callDiagnosticServiceController =
+                    new CallDiagnosticServiceController(
+                            new CallDiagnosticServiceController.ContextProxy() {
+                                @Override
+                                public List<ResolveInfo> queryIntentServicesAsUser(
+                                        @NonNull Intent intent, int flags, int userId) {
+                                    return mContext.getPackageManager().queryIntentServicesAsUser(
+                                            intent, flags, userId);
+                                }
 
-                            @Override
-                            public boolean bindServiceAsUser(@NonNull Intent service,
-                                    @NonNull ServiceConnection conn, int flags,
-                                    @NonNull UserHandle user) {
-                                return mContext.bindServiceAsUser(service, conn, flags, user);
-                            }
+                                @Override
+                                public boolean bindServiceAsUser(@NonNull Intent service,
+                                        @NonNull ServiceConnection conn, int flags,
+                                        @NonNull UserHandle user) {
+                                    return mContext.bindServiceAsUser(service, conn, flags, user);
+                                }
 
-                            @Override
-                            public void unbindService(@NonNull ServiceConnection conn) {
-                                mContext.unbindService(conn);
-                            }
+                                @Override
+                                public void unbindService(@NonNull ServiceConnection conn) {
+                                    mContext.unbindService(conn);
+                                }
 
-                            @Override
-                            public UserHandle getCurrentUserHandle() {
-                                return mCallsManager.getCurrentUserHandle();
-                            }
-                        },
-                        mContext.getResources().getString(
-                                com.android.server.telecom.R.string
-                                        .call_diagnostic_service_package_name),
-                        mLock
-                );
+                                @Override
+                                public UserHandle getCurrentUserHandle() {
+                                    return mCallsManager.getCurrentUserHandle();
+                                }
+                            },
+                            mContext.getResources().getString(
+                                    com.android.server.telecom.R.string
+                                            .call_diagnostic_service_package_name),
+                            mLock
+                    );
 
-        AudioProcessingNotification audioProcessingNotification =
-                new AudioProcessingNotification(mContext);
+            AudioProcessingNotification audioProcessingNotification =
+                    new AudioProcessingNotification(mContext);
 
-        ToastFactory toastFactory = new ToastFactory() {
-            @Override
-            public Toast makeText(Context context, int resId, int duration) {
-                return Toast.makeText(context, context.getMainLooper(), context.getString(resId),
-                        duration);
+            ToastFactory toastFactory = new ToastFactory() {
+                @Override
+                public Toast makeText(Context context, int resId, int duration) {
+                    return Toast.makeText(context, context.getMainLooper(),
+                            context.getString(resId),
+                            duration);
+                }
+
+                @Override
+                public Toast makeText(Context context, CharSequence text, int duration) {
+                    return Toast.makeText(context, context.getMainLooper(), text, duration);
+                }
+            };
+
+            mCallsManager = new CallsManager(
+                    mContext,
+                    mLock,
+                    callerInfoLookupHelper,
+                    mMissedCallNotifier,
+                    disconnectedCallNotifierFactory,
+                    mPhoneAccountRegistrar,
+                    headsetMediaButtonFactory,
+                    proximitySensorManagerFactory,
+                    inCallWakeLockControllerFactory,
+                    connectionServiceFocusManagerFactory,
+                    audioServiceFactory,
+                    bluetoothRouteManager,
+                    wiredHeadsetManager,
+                    systemStateHelper,
+                    defaultDialerCache,
+                    timeoutsAdapter,
+                    asyncRingtonePlayer,
+                    phoneNumberUtilsAdapter,
+                    emergencyCallHelper,
+                    toneGeneratorFactory,
+                    clockProxy,
+                    audioProcessingNotification,
+                    bluetoothStateReceiver,
+                    callAudioRouteStateMachineFactory,
+                    callAudioModeStateMachineFactory,
+                    inCallControllerFactory,
+                    callDiagnosticServiceController,
+                    roleManagerAdapter,
+                    toastFactory);
+
+            mIncomingCallNotifier = incomingCallNotifier;
+            incomingCallNotifier.setCallsManagerProxy(new IncomingCallNotifier.CallsManagerProxy() {
+                @Override
+                public boolean hasUnholdableCallsForOtherConnectionService(
+                        PhoneAccountHandle phoneAccountHandle) {
+                    return mCallsManager.hasUnholdableCallsForOtherConnectionService(
+                            phoneAccountHandle);
+                }
+
+                @Override
+                public int getNumUnholdableCallsForOtherConnectionService(
+                        PhoneAccountHandle phoneAccountHandle) {
+                    return mCallsManager.getNumUnholdableCallsForOtherConnectionService(
+                            phoneAccountHandle);
+                }
+
+                @Override
+                public Call getActiveCall() {
+                    return mCallsManager.getActiveCall();
+                }
+            });
+            mCallsManager.setIncomingCallNotifier(mIncomingCallNotifier);
+
+            mRespondViaSmsManager = new RespondViaSmsManager(mCallsManager, mLock);
+            mCallsManager.setRespondViaSmsManager(mRespondViaSmsManager);
+
+            mContext.registerReceiverAsUser(mUserSwitchedReceiver, UserHandle.ALL,
+                    USER_SWITCHED_FILTER, null, null);
+            mContext.registerReceiverAsUser(mUserStartingReceiver, UserHandle.ALL,
+                    USER_STARTING_FILTER, null, null);
+            mContext.registerReceiverAsUser(mBootCompletedReceiver, UserHandle.ALL,
+                    BOOT_COMPLETE_FILTER, null, null);
+
+            // Set current user explicitly since USER_SWITCHED_FILTER intent can be missed at
+            // startup
+            synchronized (mLock) {
+                UserHandle currentUserHandle = UserHandle.of(ActivityManager.getCurrentUser());
+                mPhoneAccountRegistrar.setCurrentUserHandle(currentUserHandle);
+                mCallsManager.onUserSwitch(currentUserHandle);
             }
 
-            @Override
-            public Toast makeText(Context context, CharSequence text, int duration) {
-                return Toast.makeText(context, context.getMainLooper(), text, duration);
-            }
-        };
+            mCallIntentProcessor = new CallIntentProcessor(mContext, mCallsManager,
+                    defaultDialerCache);
+            mTelecomBroadcastIntentProcessor = new TelecomBroadcastIntentProcessor(
+                    mContext, mCallsManager);
 
-        mCallsManager = new CallsManager(
-                mContext,
-                mLock,
-                callerInfoLookupHelper,
-                mMissedCallNotifier,
-                disconnectedCallNotifierFactory,
-                mPhoneAccountRegistrar,
-                headsetMediaButtonFactory,
-                proximitySensorManagerFactory,
-                inCallWakeLockControllerFactory,
-                connectionServiceFocusManagerFactory,
-                audioServiceFactory,
-                bluetoothRouteManager,
-                wiredHeadsetManager,
-                systemStateHelper,
-                defaultDialerCache,
-                timeoutsAdapter,
-                asyncRingtonePlayer,
-                phoneNumberUtilsAdapter,
-                emergencyCallHelper,
-                toneGeneratorFactory,
-                clockProxy,
-                audioProcessingNotification,
-                bluetoothStateReceiver,
-                callAudioRouteStateMachineFactory,
-                callAudioModeStateMachineFactory,
-                inCallControllerFactory,
-                callDiagnosticServiceController,
-                roleManagerAdapter,
-                toastFactory);
+            // Register the receiver for the dialer secret codes, used to enable extended logging.
+            mDialerCodeReceiver = new DialerCodeReceiver(mCallsManager);
+            mContext.registerReceiver(mDialerCodeReceiver, DIALER_SECRET_CODE_FILTER,
+                    Manifest.permission.CONTROL_INCALL_EXPERIENCE, null);
 
-        mIncomingCallNotifier = incomingCallNotifier;
-        incomingCallNotifier.setCallsManagerProxy(new IncomingCallNotifier.CallsManagerProxy() {
-            @Override
-            public boolean hasUnholdableCallsForOtherConnectionService(
-                    PhoneAccountHandle phoneAccountHandle) {
-                return mCallsManager.hasUnholdableCallsForOtherConnectionService(
-                        phoneAccountHandle);
-            }
-
-            @Override
-            public int getNumUnholdableCallsForOtherConnectionService(
-                    PhoneAccountHandle phoneAccountHandle) {
-                return mCallsManager.getNumUnholdableCallsForOtherConnectionService(
-                        phoneAccountHandle);
-            }
-
-            @Override
-            public Call getActiveCall() {
-                return mCallsManager.getActiveCall();
-            }
-        });
-        mCallsManager.setIncomingCallNotifier(mIncomingCallNotifier);
-
-        mRespondViaSmsManager = new RespondViaSmsManager(mCallsManager, mLock);
-        mCallsManager.setRespondViaSmsManager(mRespondViaSmsManager);
-
-        mContext.registerReceiverAsUser(mUserSwitchedReceiver, UserHandle.ALL,
-                USER_SWITCHED_FILTER, null, null);
-        mContext.registerReceiverAsUser(mUserStartingReceiver, UserHandle.ALL,
-                USER_STARTING_FILTER, null, null);
-        mContext.registerReceiverAsUser(mBootCompletedReceiver, UserHandle.ALL,
-                BOOT_COMPLETE_FILTER, null, null);
-
-        // Set current user explicitly since USER_SWITCHED_FILTER intent can be missed at startup
-        synchronized(mLock) {
-            UserHandle currentUserHandle = UserHandle.of(ActivityManager.getCurrentUser());
-            mPhoneAccountRegistrar.setCurrentUserHandle(currentUserHandle);
-            mCallsManager.onUserSwitch(currentUserHandle);
+            // There is no USER_SWITCHED broadcast for user 0, handle it here explicitly.
+            final UserManager userManager = UserManager.get(mContext);
+            mTelecomServiceImpl = new TelecomServiceImpl(
+                    mContext, mCallsManager, mPhoneAccountRegistrar,
+                    new CallIntentProcessor.AdapterImpl(defaultDialerCache),
+                    new UserCallIntentProcessorFactory() {
+                        @Override
+                        public UserCallIntentProcessor create(Context context,
+                                UserHandle userHandle) {
+                            return new UserCallIntentProcessor(context, userHandle);
+                        }
+                    },
+                    defaultDialerCache,
+                    new TelecomServiceImpl.SubscriptionManagerAdapterImpl(),
+                    new TelecomServiceImpl.SettingsSecureAdapterImpl(),
+                    mLock);
+        } finally {
+            Log.endSession();
         }
-
-        mCallIntentProcessor = new CallIntentProcessor(mContext, mCallsManager, defaultDialerCache);
-        mTelecomBroadcastIntentProcessor = new TelecomBroadcastIntentProcessor(
-                mContext, mCallsManager);
-
-        // Register the receiver for the dialer secret codes, used to enable extended logging.
-        mDialerCodeReceiver = new DialerCodeReceiver(mCallsManager);
-        mContext.registerReceiver(mDialerCodeReceiver, DIALER_SECRET_CODE_FILTER,
-                Manifest.permission.CONTROL_INCALL_EXPERIENCE, null);
-
-        // There is no USER_SWITCHED broadcast for user 0, handle it here explicitly.
-        final UserManager userManager = UserManager.get(mContext);
-        mTelecomServiceImpl = new TelecomServiceImpl(
-                mContext, mCallsManager, mPhoneAccountRegistrar,
-                new CallIntentProcessor.AdapterImpl(defaultDialerCache),
-                new UserCallIntentProcessorFactory() {
-                    @Override
-                    public UserCallIntentProcessor create(Context context, UserHandle userHandle) {
-                        return new UserCallIntentProcessor(context, userHandle);
-                    }
-                },
-                defaultDialerCache,
-                new TelecomServiceImpl.SubscriptionManagerAdapterImpl(),
-                new TelecomServiceImpl.SettingsSecureAdapterImpl(),
-                mLock);
-        Log.endSession();
     }
 
     @VisibleForTesting
diff --git a/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java b/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java
index f4c77f3..08239bf 100644
--- a/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java
+++ b/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java
@@ -377,6 +377,11 @@
         }
 
         @Override
+        public int checkSelfPermission(String permission) {
+            return PackageManager.PERMISSION_GRANTED;
+        }
+
+        @Override
         public void enforceCallingOrSelfPermission(String permission, String message) {
             // Don't bother enforcing anything in mock.
         }
diff --git a/tests/src/com/android/server/telecom/tests/SessionTest.java b/tests/src/com/android/server/telecom/tests/SessionTest.java
index 6a14a64..4be3dad 100644
--- a/tests/src/com/android/server/telecom/tests/SessionTest.java
+++ b/tests/src/com/android/server/telecom/tests/SessionTest.java
@@ -173,6 +173,36 @@
     }
 
     /**
+     * Ensure creating two sessions and setting the child as the parent to itself doesn't cause a
+     * crash due to infinite recursion.
+     */
+    @SmallTest
+    @Test
+    public void testRecursion_toString_childCircDep() {
+        Log.startSession("testParent");
+        // Running in the same thread, so mark as invisible subsession
+        Session childSession = Log.getSessionManager()
+                .createSubsession(true /*isStartedFromActiveSession*/);
+        Log.continueSession(childSession, "child");
+        Session parentSession = childSession.getParentSession();
+        // Create a circular dependency and ensure we do not crash
+        childSession.setParentSession(childSession);
+
+        // Make sure calling these methods does not result in a crash
+        try {
+            parentSession.toString();
+            childSession.toString();
+        } catch (Exception e) {
+            fail("Exception: " + e.getMessage());
+        } finally {
+            // End child
+            Log.endSession();
+            // End parent
+            Log.endSession();
+        }
+    }
+
+    /**
      * Ensure creating two sessions that are parent/child of each other does not lead to a crash
      * or infinite recursion when using Session#getInfo.
      */
diff --git a/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java b/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
index 5c1cdc4..7f462d4 100644
--- a/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
+++ b/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
@@ -365,7 +365,7 @@
 
         // Next, create the TelecomSystem, our system under test
         setupTelecomSystem();
-        // Need to reset teseting tag here
+        // Need to reset testing tag here
         Log.setTag(TESTING_TAG);
 
         // Finally, register the ConnectionServices with the PhoneAccountRegistrar of the
@@ -401,11 +401,9 @@
         mConnectionServiceFixtureA.waitForHandlerToClear();
         mConnectionServiceFixtureB.waitForHandlerToClear();
 
-        // Print out any incomplete sessions for debugging tests
-        String sessions = Log.getSessionManager().printActiveSessions();
-        if (!TextUtils.isEmpty(sessions)) {
-            Log.w(this, "Active Sessions:\n" + sessions);
-        }
+        // Forcefully clean all sessions at the end of the test, which will also log any stale
+        // sessions for debugging.
+        Log.getSessionManager().cleanupStaleSessions(0);
 
         mTelecomSystem = null;
         super.tearDown();