Loading src/com/android/server/telecom/CallAudioManager.java +2 −2 Original line number Diff line number Diff line Loading @@ -542,9 +542,9 @@ public class CallAudioManager extends CallsManagerListenerBase { mCallAudioModeStateMachine.dump(pw); pw.decreaseIndent(); pw.println("CallAudioRouteStateMachine pending messages:"); pw.println("CallAudioRouteStateMachine:"); pw.increaseIndent(); mCallAudioRouteStateMachine.dumpPendingMessages(pw); mCallAudioRouteStateMachine.dump(pw); pw.decreaseIndent(); pw.println("BluetoothDeviceManager:"); Loading src/com/android/server/telecom/CallAudioRouteStateMachine.java +15 −4 Original line number Diff line number Diff line Loading @@ -411,6 +411,8 @@ public class CallAudioRouteStateMachine extends StateMachine { Log.w(this, "Ignoring switch to headset command. Not available."); } return HANDLED; case CONNECT_DOCK: // fall through; we want to switch to speaker mode when docked and in a call. case SWITCH_SPEAKER: case USER_SWITCH_SPEAKER: setSpeakerphoneOn(true); Loading Loading @@ -486,6 +488,8 @@ public class CallAudioRouteStateMachine extends StateMachine { Log.w(this, "Ignoring switch to headset command. Not available."); } return HANDLED; case CONNECT_DOCK: // fall through; we want to go to the quiescent speaker route when out of a call case SWITCH_SPEAKER: case USER_SWITCH_SPEAKER: transitionTo(mQuiescentSpeakerRoute); Loading Loading @@ -537,10 +541,6 @@ public class CallAudioRouteStateMachine extends StateMachine { case BT_AUDIO_DISCONNECTED: // This may be sent as a confirmation by the BT stack after switch off BT. return HANDLED; case CONNECT_DOCK: setSpeakerphoneOn(true); sendInternalMessage(SWITCH_SPEAKER); return HANDLED; case DISCONNECT_DOCK: // Nothing to do here return HANDLED; Loading Loading @@ -1273,6 +1273,8 @@ public class CallAudioRouteStateMachine extends StateMachine { case SPEAKER_ON: // Nothing to do return HANDLED; case DISCONNECT_DOCK: // Fall-through; same as if speaker goes off, we want to switch baseline. case SPEAKER_OFF: sendInternalMessage(SWITCH_BASELINE_ROUTE, INCLUDE_BLUETOOTH_IN_BASELINE); return HANDLED; Loading Loading @@ -1619,6 +1621,15 @@ public class CallAudioRouteStateMachine extends StateMachine { quitNow(); } public void dump(IndentingPrintWriter pw) { pw.print("Current state: "); pw.println(getCurrentState().getName()); pw.println("Pending messages:"); pw.increaseIndent(); dumpPendingMessages(pw); pw.decreaseIndent(); } public void dumpPendingMessages(IndentingPrintWriter pw) { getHandler().getLooper().dump(pw::println, ""); } Loading src/com/android/server/telecom/DockManager.java +6 −1 Original line number Diff line number Diff line Loading @@ -29,7 +29,12 @@ import java.util.Collections; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; /** Listens for and caches car dock state. */ /** * Listens for and caches car dock state. * Testing; you can enable/disable dock with adb commands: * adb shell dumpsys DockObserver set state 3 * adb shell dumpsys DockObserver set state 0 */ @VisibleForTesting public class DockManager { @VisibleForTesting Loading src/com/android/server/telecom/InCallTonePlayer.java +68 −67 Original line number Diff line number Diff line Loading @@ -33,6 +33,7 @@ import com.android.internal.annotations.VisibleForTesting; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; /** * Play a call-related tone (ringback, busy signal, etc.) either through ToneGenerator, or using a Loading Loading @@ -186,7 +187,7 @@ public class InCallTonePlayer extends Thread { * when we need focus and when it can be release. This should only be manipulated from the main * thread. */ private static int sTonesPlaying = 0; private static AtomicInteger sTonesPlaying = new AtomicInteger(0); private final CallAudioManager mCallAudioManager; private final CallAudioRoutePeripheralAdapter mCallAudioRoutePeripheralAdapter; Loading @@ -212,6 +213,12 @@ public class InCallTonePlayer extends Thread { private final MediaPlayerFactory mMediaPlayerFactory; private final AudioManagerAdapter mAudioManagerAdapter; /** * Latch used for awaiting on playback, which may be interrupted if the tone is stopped from * outside the playback. */ private final CountDownLatch mPlaybackLatch = new CountDownLatch(1); /** * Initializes the tone player. Private; use the {@link Factory} to create tone players. * Loading Loading @@ -388,26 +395,21 @@ public class InCallTonePlayer extends Thread { } Log.i(this, "playToneGeneratorTone: toneType=%d", toneType); // TODO: Certain CDMA tones need to check the ringer-volume state before // playing. See CallNotifier.InCallTonePlayer. // TODO: Some tones play through the end of a call so we need to inform // CallAudioManager that we want focus the same way that Ringer does. synchronized (this) { if (mState != STATE_STOPPED) { mState = STATE_ON; toneGenerator.startTone(toneType); try { Log.v(this, "Starting tone %d...waiting for %d ms.", mToneId, toneLengthMillis + TIMEOUT_BUFFER_MILLIS); wait(toneLengthMillis + TIMEOUT_BUFFER_MILLIS); } catch (InterruptedException e) { Log.w(this, "wait interrupted", e); } if (mPlaybackLatch.await(toneLengthMillis + TIMEOUT_BUFFER_MILLIS, TimeUnit.MILLISECONDS)) { Log.i(this, "playToneGeneratorTone: tone playback stopped."); } } catch (InterruptedException e) { Log.w(this, "playToneGeneratorTone: wait interrupted", e); } mState = STATE_OFF; // Redundant; don't want anyone re-using at this point. mState = STATE_STOPPED; } finally { if (toneGenerator != null) { toneGenerator.release(); Loading @@ -421,10 +423,7 @@ public class InCallTonePlayer extends Thread { * @param toneResourceId The resource ID of the tone to play. */ private void playMediaTone(int stream, int toneResourceId) { synchronized (this) { if (mState != STATE_STOPPED) { mState = STATE_ON; } Log.i(this, "playMediaTone: toneResourceId=%d", toneResourceId); AudioAttributes attributes = new AudioAttributes.Builder() .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION) Loading @@ -434,32 +433,32 @@ public class InCallTonePlayer extends Thread { mToneMediaPlayer = mMediaPlayerFactory.get(toneResourceId, attributes); mToneMediaPlayer.setLooping(false); int durationMillis = mToneMediaPlayer.getDuration(); final CountDownLatch toneLatch = new CountDownLatch(1); mToneMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { @Override public void onCompletion(MediaPlayer mp) { Log.i(InCallTonePlayer.this, "playMediaTone: toneResourceId=%d completed.", toneResourceId); synchronized (InCallTonePlayer.this) { mState = STATE_OFF; } mToneMediaPlayer.release(); mToneMediaPlayer = null; toneLatch.countDown(); mPlaybackLatch.countDown(); } }); mToneMediaPlayer.start(); try { mToneMediaPlayer.start(); // Wait for the tone to stop playing; timeout at 2x the length of the file just to // be on the safe side. toneLatch.await(durationMillis * 2, TimeUnit.MILLISECONDS); // be on the safe side. Playback can also be stopped via stopTone(). if (mPlaybackLatch.await(durationMillis * 2, TimeUnit.MILLISECONDS)) { Log.i(this, "playMediaTone: tone playback stopped."); } } catch (InterruptedException ie) { Log.e(this, ie, "playMediaTone: tone playback interrupted."); } finally { // Redundant; don't want anyone re-using at this point. mState = STATE_STOPPED; mToneMediaPlayer.release(); mToneMediaPlayer = null; } } } @VisibleForTesting public boolean startTone() { // Skip playing the end call tone if the volume is silenced. Loading @@ -468,8 +467,12 @@ public class InCallTonePlayer extends Thread { return false; } sTonesPlaying++; if (sTonesPlaying == 1) { // Tone already done; don't allow re-used if (mState == STATE_STOPPED) { return false; } if (sTonesPlaying.incrementAndGet() == 1) { mCallAudioManager.setIsTonePlaying(true); } Loading @@ -494,18 +497,17 @@ public class InCallTonePlayer extends Thread { */ @VisibleForTesting public void stopTone() { synchronized (this) { if (mState == STATE_ON) { Log.d(this, "Stopping the tone %d.", mToneId); notify(); Log.i(this, "stopTone: Stopping the tone %d.", mToneId); // Notify the playback to end early. mPlaybackLatch.countDown(); } mState = STATE_STOPPED; } } @VisibleForTesting public void cleanup() { sTonesPlaying = 0; sTonesPlaying.set(0); } private void cleanUpTonePlayer() { Loading @@ -514,12 +516,11 @@ public class InCallTonePlayer extends Thread { mMainThreadHandler.post(new Runnable("ICTP.cUTP", mLock) { @Override public void loggedRun() { if (sTonesPlaying == 0) { Log.wtf(InCallTonePlayer.this, "cleanUpTonePlayer(): Over-releasing focus for tone player."); } else if (--sTonesPlaying == 0) { int newToneCount = sTonesPlaying.updateAndGet( t -> Math.min(0, t--)); if (newToneCount == 0) { Log.i(InCallTonePlayer.this, "cleanUpTonePlayer(): tonesPlaying=%d, tone completed", sTonesPlaying); "cleanUpTonePlayer(): tonesPlaying=%d, tone completed", newToneCount); if (mCallAudioManager != null) { mCallAudioManager.setIsTonePlaying(false); } else { Loading @@ -528,7 +529,7 @@ public class InCallTonePlayer extends Thread { } } else { Log.i(InCallTonePlayer.this, "cleanUpTonePlayer(): tonesPlaying=%d; still playing", sTonesPlaying); "cleanUpTonePlayer(): tonesPlaying=%d; still playing", newToneCount); } } }.prepare()); Loading src/com/android/server/telecom/PhoneAccountRegistrar.java +47 −1 Original line number Diff line number Diff line Loading @@ -47,6 +47,7 @@ import android.telecom.PhoneAccount; import android.telecom.PhoneAccountHandle; import android.telephony.CarrierConfigManager; import android.telephony.PhoneNumberUtils; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.text.TextUtils; Loading Loading @@ -1413,7 +1414,7 @@ public class PhoneAccountRegistrar { public final UserHandle userHandle; public final PhoneAccountHandle phoneAccountHandle; public PhoneAccountHandle phoneAccountHandle; public final String groupId; Loading Loading @@ -1555,6 +1556,7 @@ public class PhoneAccountRegistrar { XmlPullParser parser = Xml.resolvePullParser(is); parser.nextTag(); mState = readFromXml(parser, mContext); migratePhoneAccountHandle(mState); versionChanged = mState.versionNumber < EXPECTED_STATE_VERSION; } catch (IOException | XmlPullParserException e) { Loading Loading @@ -1599,6 +1601,50 @@ public class PhoneAccountRegistrar { return s != null ? s : new State(); } /** * Try to migrate the ID of default phone account handle from IccId to SubId. */ @VisibleForTesting public void migratePhoneAccountHandle(State state) { if (mSubscriptionManager == null) { return; } // Use getAllSubscirptionInfoList() to get the mapping between iccId and subId // from the subscription database List<SubscriptionInfo> subscriptionInfos = mSubscriptionManager .getAllSubscriptionInfoList(); Map<UserHandle, DefaultPhoneAccountHandle> defaultPhoneAccountHandles = state.defaultOutgoingAccountHandles; for (Map.Entry<UserHandle, DefaultPhoneAccountHandle> entry : defaultPhoneAccountHandles.entrySet()) { DefaultPhoneAccountHandle defaultPhoneAccountHandle = entry.getValue(); // Migrate Telephony PhoneAccountHandle only String telephonyComponentName = "com.android.phone/com.android.services.telephony.TelephonyConnectionService"; if (!defaultPhoneAccountHandle.phoneAccountHandle.getComponentName() .flattenToString().equals(telephonyComponentName)) { continue; } // Migrate from IccId to SubId for (SubscriptionInfo subscriptionInfo : subscriptionInfos) { String phoneAccountHandleId = defaultPhoneAccountHandle.phoneAccountHandle.getId(); // Some phone account handle would store phone account handle id with the IccId // string plus "F", and the getIccId() returns IccId string itself without "F", // so here need to use "startsWith" to match. if (phoneAccountHandleId != null && phoneAccountHandleId.startsWith( subscriptionInfo.getIccId())) { Log.i(this, "Found subscription ID to migrate: " + subscriptionInfo.getSubscriptionId()); defaultPhoneAccountHandle.phoneAccountHandle = new PhoneAccountHandle( defaultPhoneAccountHandle.phoneAccountHandle.getComponentName(), Integer.toString(subscriptionInfo.getSubscriptionId())); break; } } } } //////////////////////////////////////////////////////////////////////////////////////////////// // // XML serialization Loading Loading
src/com/android/server/telecom/CallAudioManager.java +2 −2 Original line number Diff line number Diff line Loading @@ -542,9 +542,9 @@ public class CallAudioManager extends CallsManagerListenerBase { mCallAudioModeStateMachine.dump(pw); pw.decreaseIndent(); pw.println("CallAudioRouteStateMachine pending messages:"); pw.println("CallAudioRouteStateMachine:"); pw.increaseIndent(); mCallAudioRouteStateMachine.dumpPendingMessages(pw); mCallAudioRouteStateMachine.dump(pw); pw.decreaseIndent(); pw.println("BluetoothDeviceManager:"); Loading
src/com/android/server/telecom/CallAudioRouteStateMachine.java +15 −4 Original line number Diff line number Diff line Loading @@ -411,6 +411,8 @@ public class CallAudioRouteStateMachine extends StateMachine { Log.w(this, "Ignoring switch to headset command. Not available."); } return HANDLED; case CONNECT_DOCK: // fall through; we want to switch to speaker mode when docked and in a call. case SWITCH_SPEAKER: case USER_SWITCH_SPEAKER: setSpeakerphoneOn(true); Loading Loading @@ -486,6 +488,8 @@ public class CallAudioRouteStateMachine extends StateMachine { Log.w(this, "Ignoring switch to headset command. Not available."); } return HANDLED; case CONNECT_DOCK: // fall through; we want to go to the quiescent speaker route when out of a call case SWITCH_SPEAKER: case USER_SWITCH_SPEAKER: transitionTo(mQuiescentSpeakerRoute); Loading Loading @@ -537,10 +541,6 @@ public class CallAudioRouteStateMachine extends StateMachine { case BT_AUDIO_DISCONNECTED: // This may be sent as a confirmation by the BT stack after switch off BT. return HANDLED; case CONNECT_DOCK: setSpeakerphoneOn(true); sendInternalMessage(SWITCH_SPEAKER); return HANDLED; case DISCONNECT_DOCK: // Nothing to do here return HANDLED; Loading Loading @@ -1273,6 +1273,8 @@ public class CallAudioRouteStateMachine extends StateMachine { case SPEAKER_ON: // Nothing to do return HANDLED; case DISCONNECT_DOCK: // Fall-through; same as if speaker goes off, we want to switch baseline. case SPEAKER_OFF: sendInternalMessage(SWITCH_BASELINE_ROUTE, INCLUDE_BLUETOOTH_IN_BASELINE); return HANDLED; Loading Loading @@ -1619,6 +1621,15 @@ public class CallAudioRouteStateMachine extends StateMachine { quitNow(); } public void dump(IndentingPrintWriter pw) { pw.print("Current state: "); pw.println(getCurrentState().getName()); pw.println("Pending messages:"); pw.increaseIndent(); dumpPendingMessages(pw); pw.decreaseIndent(); } public void dumpPendingMessages(IndentingPrintWriter pw) { getHandler().getLooper().dump(pw::println, ""); } Loading
src/com/android/server/telecom/DockManager.java +6 −1 Original line number Diff line number Diff line Loading @@ -29,7 +29,12 @@ import java.util.Collections; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; /** Listens for and caches car dock state. */ /** * Listens for and caches car dock state. * Testing; you can enable/disable dock with adb commands: * adb shell dumpsys DockObserver set state 3 * adb shell dumpsys DockObserver set state 0 */ @VisibleForTesting public class DockManager { @VisibleForTesting Loading
src/com/android/server/telecom/InCallTonePlayer.java +68 −67 Original line number Diff line number Diff line Loading @@ -33,6 +33,7 @@ import com.android.internal.annotations.VisibleForTesting; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; /** * Play a call-related tone (ringback, busy signal, etc.) either through ToneGenerator, or using a Loading Loading @@ -186,7 +187,7 @@ public class InCallTonePlayer extends Thread { * when we need focus and when it can be release. This should only be manipulated from the main * thread. */ private static int sTonesPlaying = 0; private static AtomicInteger sTonesPlaying = new AtomicInteger(0); private final CallAudioManager mCallAudioManager; private final CallAudioRoutePeripheralAdapter mCallAudioRoutePeripheralAdapter; Loading @@ -212,6 +213,12 @@ public class InCallTonePlayer extends Thread { private final MediaPlayerFactory mMediaPlayerFactory; private final AudioManagerAdapter mAudioManagerAdapter; /** * Latch used for awaiting on playback, which may be interrupted if the tone is stopped from * outside the playback. */ private final CountDownLatch mPlaybackLatch = new CountDownLatch(1); /** * Initializes the tone player. Private; use the {@link Factory} to create tone players. * Loading Loading @@ -388,26 +395,21 @@ public class InCallTonePlayer extends Thread { } Log.i(this, "playToneGeneratorTone: toneType=%d", toneType); // TODO: Certain CDMA tones need to check the ringer-volume state before // playing. See CallNotifier.InCallTonePlayer. // TODO: Some tones play through the end of a call so we need to inform // CallAudioManager that we want focus the same way that Ringer does. synchronized (this) { if (mState != STATE_STOPPED) { mState = STATE_ON; toneGenerator.startTone(toneType); try { Log.v(this, "Starting tone %d...waiting for %d ms.", mToneId, toneLengthMillis + TIMEOUT_BUFFER_MILLIS); wait(toneLengthMillis + TIMEOUT_BUFFER_MILLIS); } catch (InterruptedException e) { Log.w(this, "wait interrupted", e); } if (mPlaybackLatch.await(toneLengthMillis + TIMEOUT_BUFFER_MILLIS, TimeUnit.MILLISECONDS)) { Log.i(this, "playToneGeneratorTone: tone playback stopped."); } } catch (InterruptedException e) { Log.w(this, "playToneGeneratorTone: wait interrupted", e); } mState = STATE_OFF; // Redundant; don't want anyone re-using at this point. mState = STATE_STOPPED; } finally { if (toneGenerator != null) { toneGenerator.release(); Loading @@ -421,10 +423,7 @@ public class InCallTonePlayer extends Thread { * @param toneResourceId The resource ID of the tone to play. */ private void playMediaTone(int stream, int toneResourceId) { synchronized (this) { if (mState != STATE_STOPPED) { mState = STATE_ON; } Log.i(this, "playMediaTone: toneResourceId=%d", toneResourceId); AudioAttributes attributes = new AudioAttributes.Builder() .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION) Loading @@ -434,32 +433,32 @@ public class InCallTonePlayer extends Thread { mToneMediaPlayer = mMediaPlayerFactory.get(toneResourceId, attributes); mToneMediaPlayer.setLooping(false); int durationMillis = mToneMediaPlayer.getDuration(); final CountDownLatch toneLatch = new CountDownLatch(1); mToneMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { @Override public void onCompletion(MediaPlayer mp) { Log.i(InCallTonePlayer.this, "playMediaTone: toneResourceId=%d completed.", toneResourceId); synchronized (InCallTonePlayer.this) { mState = STATE_OFF; } mToneMediaPlayer.release(); mToneMediaPlayer = null; toneLatch.countDown(); mPlaybackLatch.countDown(); } }); mToneMediaPlayer.start(); try { mToneMediaPlayer.start(); // Wait for the tone to stop playing; timeout at 2x the length of the file just to // be on the safe side. toneLatch.await(durationMillis * 2, TimeUnit.MILLISECONDS); // be on the safe side. Playback can also be stopped via stopTone(). if (mPlaybackLatch.await(durationMillis * 2, TimeUnit.MILLISECONDS)) { Log.i(this, "playMediaTone: tone playback stopped."); } } catch (InterruptedException ie) { Log.e(this, ie, "playMediaTone: tone playback interrupted."); } finally { // Redundant; don't want anyone re-using at this point. mState = STATE_STOPPED; mToneMediaPlayer.release(); mToneMediaPlayer = null; } } } @VisibleForTesting public boolean startTone() { // Skip playing the end call tone if the volume is silenced. Loading @@ -468,8 +467,12 @@ public class InCallTonePlayer extends Thread { return false; } sTonesPlaying++; if (sTonesPlaying == 1) { // Tone already done; don't allow re-used if (mState == STATE_STOPPED) { return false; } if (sTonesPlaying.incrementAndGet() == 1) { mCallAudioManager.setIsTonePlaying(true); } Loading @@ -494,18 +497,17 @@ public class InCallTonePlayer extends Thread { */ @VisibleForTesting public void stopTone() { synchronized (this) { if (mState == STATE_ON) { Log.d(this, "Stopping the tone %d.", mToneId); notify(); Log.i(this, "stopTone: Stopping the tone %d.", mToneId); // Notify the playback to end early. mPlaybackLatch.countDown(); } mState = STATE_STOPPED; } } @VisibleForTesting public void cleanup() { sTonesPlaying = 0; sTonesPlaying.set(0); } private void cleanUpTonePlayer() { Loading @@ -514,12 +516,11 @@ public class InCallTonePlayer extends Thread { mMainThreadHandler.post(new Runnable("ICTP.cUTP", mLock) { @Override public void loggedRun() { if (sTonesPlaying == 0) { Log.wtf(InCallTonePlayer.this, "cleanUpTonePlayer(): Over-releasing focus for tone player."); } else if (--sTonesPlaying == 0) { int newToneCount = sTonesPlaying.updateAndGet( t -> Math.min(0, t--)); if (newToneCount == 0) { Log.i(InCallTonePlayer.this, "cleanUpTonePlayer(): tonesPlaying=%d, tone completed", sTonesPlaying); "cleanUpTonePlayer(): tonesPlaying=%d, tone completed", newToneCount); if (mCallAudioManager != null) { mCallAudioManager.setIsTonePlaying(false); } else { Loading @@ -528,7 +529,7 @@ public class InCallTonePlayer extends Thread { } } else { Log.i(InCallTonePlayer.this, "cleanUpTonePlayer(): tonesPlaying=%d; still playing", sTonesPlaying); "cleanUpTonePlayer(): tonesPlaying=%d; still playing", newToneCount); } } }.prepare()); Loading
src/com/android/server/telecom/PhoneAccountRegistrar.java +47 −1 Original line number Diff line number Diff line Loading @@ -47,6 +47,7 @@ import android.telecom.PhoneAccount; import android.telecom.PhoneAccountHandle; import android.telephony.CarrierConfigManager; import android.telephony.PhoneNumberUtils; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.text.TextUtils; Loading Loading @@ -1413,7 +1414,7 @@ public class PhoneAccountRegistrar { public final UserHandle userHandle; public final PhoneAccountHandle phoneAccountHandle; public PhoneAccountHandle phoneAccountHandle; public final String groupId; Loading Loading @@ -1555,6 +1556,7 @@ public class PhoneAccountRegistrar { XmlPullParser parser = Xml.resolvePullParser(is); parser.nextTag(); mState = readFromXml(parser, mContext); migratePhoneAccountHandle(mState); versionChanged = mState.versionNumber < EXPECTED_STATE_VERSION; } catch (IOException | XmlPullParserException e) { Loading Loading @@ -1599,6 +1601,50 @@ public class PhoneAccountRegistrar { return s != null ? s : new State(); } /** * Try to migrate the ID of default phone account handle from IccId to SubId. */ @VisibleForTesting public void migratePhoneAccountHandle(State state) { if (mSubscriptionManager == null) { return; } // Use getAllSubscirptionInfoList() to get the mapping between iccId and subId // from the subscription database List<SubscriptionInfo> subscriptionInfos = mSubscriptionManager .getAllSubscriptionInfoList(); Map<UserHandle, DefaultPhoneAccountHandle> defaultPhoneAccountHandles = state.defaultOutgoingAccountHandles; for (Map.Entry<UserHandle, DefaultPhoneAccountHandle> entry : defaultPhoneAccountHandles.entrySet()) { DefaultPhoneAccountHandle defaultPhoneAccountHandle = entry.getValue(); // Migrate Telephony PhoneAccountHandle only String telephonyComponentName = "com.android.phone/com.android.services.telephony.TelephonyConnectionService"; if (!defaultPhoneAccountHandle.phoneAccountHandle.getComponentName() .flattenToString().equals(telephonyComponentName)) { continue; } // Migrate from IccId to SubId for (SubscriptionInfo subscriptionInfo : subscriptionInfos) { String phoneAccountHandleId = defaultPhoneAccountHandle.phoneAccountHandle.getId(); // Some phone account handle would store phone account handle id with the IccId // string plus "F", and the getIccId() returns IccId string itself without "F", // so here need to use "startsWith" to match. if (phoneAccountHandleId != null && phoneAccountHandleId.startsWith( subscriptionInfo.getIccId())) { Log.i(this, "Found subscription ID to migrate: " + subscriptionInfo.getSubscriptionId()); defaultPhoneAccountHandle.phoneAccountHandle = new PhoneAccountHandle( defaultPhoneAccountHandle.phoneAccountHandle.getComponentName(), Integer.toString(subscriptionInfo.getSubscriptionId())); break; } } } } //////////////////////////////////////////////////////////////////////////////////////////////// // // XML serialization Loading