diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 27ecf53204a..9998d08e788 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -4647,6 +4647,9 @@ + + diff --git a/res/values/integers.xml b/res/values/integers.xml index 530f9874a31..9d28aaa53e9 100644 --- a/res/values/integers.xml +++ b/res/values/integers.xml @@ -21,6 +21,7 @@ 102 103 104 + 105 1 diff --git a/src/com/android/settings/sim/receivers/SimSlotChangeReceiver.java b/src/com/android/settings/sim/receivers/SimSlotChangeReceiver.java index f144c6b5ad0..af8b38c5cf2 100644 --- a/src/com/android/settings/sim/receivers/SimSlotChangeReceiver.java +++ b/src/com/android/settings/sim/receivers/SimSlotChangeReceiver.java @@ -37,9 +37,6 @@ import java.util.List; public class SimSlotChangeReceiver extends BroadcastReceiver { private static final String TAG = "SlotChangeReceiver"; - private final SimSlotChangeHandler mSlotChangeHandler = SimSlotChangeHandler.get(); - private final Object mLock = new Object(); - @Override public void onReceive(Context context, Intent intent) { @@ -49,20 +46,17 @@ public class SimSlotChangeReceiver extends BroadcastReceiver { return; } - final PendingResult pendingResult = goAsync(); - ThreadUtils.postOnBackgroundThread( - () -> { - synchronized (mLock) { - if (shouldHandleSlotChange(context)) { - mSlotChangeHandler.onSlotsStatusChange(context.getApplicationContext()); - } - } - ThreadUtils.postOnMainThread(pendingResult::finish); - }); + SimSlotChangeService.scheduleSimSlotChange(context); + } + + public static void runOnBackgroundThread(Context context) { + if (shouldHandleSlotChange(context)) { + SimSlotChangeHandler.get().onSlotsStatusChange(context.getApplicationContext()); + } } // Checks whether the slot event should be handled. - private boolean shouldHandleSlotChange(Context context) { + private static boolean shouldHandleSlotChange(Context context) { if (!context.getResources().getBoolean(R.bool.config_handle_sim_slot_change)) { Log.i(TAG, "The flag is off. Ignore slot changes."); return false; @@ -88,7 +82,7 @@ public class SimSlotChangeReceiver extends BroadcastReceiver { } // Checks whether the SIM slot state is valid for slot change event. - private boolean isSimSlotStateValid(Context context) { + private static boolean isSimSlotStateValid(Context context) { final TelephonyManager telMgr = context.getSystemService(TelephonyManager.class); UiccSlotInfo[] slotInfos = telMgr.getUiccSlotsInfo(); if (slotInfos == null) { @@ -136,7 +130,8 @@ public class SimSlotChangeReceiver extends BroadcastReceiver { } @Nullable - private UiccCardInfo findUiccCardInfoBySlot(TelephonyManager telMgr, int physicalSlotIndex) { + private static UiccCardInfo findUiccCardInfoBySlot(TelephonyManager telMgr, + int physicalSlotIndex) { List cardInfos = telMgr.getUiccCardsInfo(); if (cardInfos == null) { return null; diff --git a/src/com/android/settings/sim/receivers/SimSlotChangeService.java b/src/com/android/settings/sim/receivers/SimSlotChangeService.java new file mode 100644 index 00000000000..deaecaf679e --- /dev/null +++ b/src/com/android/settings/sim/receivers/SimSlotChangeService.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2022 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.settings.sim.receivers; + +import android.app.job.JobInfo; +import android.app.job.JobParameters; +import android.app.job.JobScheduler; +import android.app.job.JobService; +import android.content.ComponentName; +import android.content.Context; +import android.os.Handler; +import android.os.HandlerThread; +import android.util.Log; + +import com.android.settings.R; +import com.android.settingslib.utils.ThreadUtils; + +import java.util.concurrent.Phaser; + +/** A JobService work on SIM slot change. */ +public class SimSlotChangeService extends JobService { + + private static final String TAG = "SimSlotChangeService"; + + /** + * Schedules a service to work on SIM slot change. + * + * @param context is the caller context. + */ + public static void scheduleSimSlotChange(Context context) { + Context appContext = context.getApplicationContext(); + JobScheduler jobScheduler = appContext.getSystemService(JobScheduler.class); + ComponentName component = new ComponentName(appContext, SimSlotChangeService.class); + + jobScheduler.schedule( + new JobInfo.Builder(R.integer.sim_slot_changed, component).build()); + } + + @Override + public boolean onStartJob(JobParameters params) { + + HandlerThread thread = new HandlerThread(TAG); + thread.start(); + final Phaser blocker = new Phaser(1); + Handler handler = new Handler(thread.getLooper()); + handler.post(() -> { + try { + SimSlotChangeReceiver.runOnBackgroundThread(this); + } catch (Throwable exception) { + Log.e(TAG, "Exception running job", exception); + } + blocker.arrive(); + }); + blocker.awaitAdvance(0); + thread.quit(); + return false; + } + + @Override + public boolean onStopJob(JobParameters params) { + return false; + } +} diff --git a/tests/unit/src/com/android/settings/sim/receivers/SimSlotChangeServiceTest.java b/tests/unit/src/com/android/settings/sim/receivers/SimSlotChangeServiceTest.java new file mode 100644 index 00000000000..029e0d7ecad --- /dev/null +++ b/tests/unit/src/com/android/settings/sim/receivers/SimSlotChangeServiceTest.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2022 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.settings.sim.receivers; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +import android.app.job.JobScheduler; +import android.content.Context; +import androidx.test.core.app.ApplicationProvider; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@RunWith(AndroidJUnit4.class) +public class SimSlotChangeServiceTest { + + @Mock + private JobScheduler mJobScheduler; + + private Context mContext; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mContext = spy(ApplicationProvider.getApplicationContext()); + doReturn(mContext).when(mContext).getApplicationContext(); + mockService(Context.JOB_SCHEDULER_SERVICE, JobScheduler.class, mJobScheduler); + } + + @Test + public void scheduleSimSlotChange_addSchedule_whenInvoked() { + SimSlotChangeService.scheduleSimSlotChange(mContext); + verify(mJobScheduler).schedule(any()); + } + + private void mockService(String serviceName, Class serviceClass, T service) { + doReturn(serviceName).when(mContext).getSystemServiceName(serviceClass); + doReturn(service).when(mContext).getSystemService(serviceName); + } +}