From 9dc0a45fd886097a49fcd97c47043cb2093c0749 Mon Sep 17 00:00:00 2001 From: Yanting Yang Date: Tue, 6 Aug 2019 22:45:52 +0800 Subject: [PATCH] Fix NPE crash when opening the app info from shortcut suggestion It occurs NPE in invoking refreshUi() of AppButtonsPreferenceController when user fully install the instant app from google play and then directly open app info from the shortcut suggestion in launcher. Root cause: AppButtonsPreferenceController.refreshUi() will be invoked when user directly open app info from the shortcut suggestion in launcher. In refreshUi(), we will update the information of button preference, but the preferences will not be initialized if device existed an instant version for the app. Solution: Check button preference and initial them if need in refreshUi(). Fixes: 137854835 Test: visual, robotests Change-Id: Id5c3e53b9db2683cab970c10eace7925d889eea3 --- .../AppButtonsPreferenceController.java | 35 ++++++++++++------- .../AppButtonsPreferenceControllerTest.java | 26 ++++++++++++++ 2 files changed, 49 insertions(+), 12 deletions(-) diff --git a/src/com/android/settings/applications/appinfo/AppButtonsPreferenceController.java b/src/com/android/settings/applications/appinfo/AppButtonsPreferenceController.java index 590384ceebb..d954e72058d 100644 --- a/src/com/android/settings/applications/appinfo/AppButtonsPreferenceController.java +++ b/src/com/android/settings/applications/appinfo/AppButtonsPreferenceController.java @@ -116,6 +116,7 @@ public class AppButtonsPreferenceController extends BasePreferenceController imp private Intent mAppLaunchIntent; private ApplicationsState.Session mSession; private RestrictedLockUtils.EnforcedAdmin mAppsControlDisallowedAdmin; + private PreferenceScreen mScreen; private boolean mUpdatedSysApp = false; private boolean mListeningToPackageRemove = false; @@ -167,19 +168,9 @@ public class AppButtonsPreferenceController extends BasePreferenceController imp @Override public void displayPreference(PreferenceScreen screen) { super.displayPreference(screen); + mScreen = screen; if (isAvailable()) { - mButtonsPref = ((ActionButtonsPreference) screen.findPreference( - KEY_ACTION_BUTTONS)) - .setButton1Text(R.string.launch_instant_app) - .setButton1Icon(R.drawable.ic_settings_open) - .setButton1OnClickListener(v -> launchApplication()) - .setButton2Text(R.string.uninstall_text) - .setButton2Icon(R.drawable.ic_settings_delete) - .setButton2OnClickListener(new UninstallAndDisableButtonListener()) - .setButton3Text(R.string.force_stop) - .setButton3Icon(R.drawable.ic_settings_force_stop) - .setButton3OnClickListener(new ForceStopButtonListener()) - .setButton3Enabled(false); + initButtonPreference(); } } @@ -663,6 +654,11 @@ public class AppButtonsPreferenceController extends BasePreferenceController imp } } + // When the app was installed from instant state, buttons preferences could be null. + if (mButtonsPref == null) { + initButtonPreference(); + mButtonsPref.setVisible(true); + } updateOpenButton(); updateUninstallButton(); updateForceStopButton(); @@ -670,6 +666,21 @@ public class AppButtonsPreferenceController extends BasePreferenceController imp return true; } + private void initButtonPreference() { + mButtonsPref = ((ActionButtonsPreference) mScreen.findPreference( + KEY_ACTION_BUTTONS)) + .setButton1Text(R.string.launch_instant_app) + .setButton1Icon(R.drawable.ic_settings_open) + .setButton1OnClickListener(v -> launchApplication()) + .setButton2Text(R.string.uninstall_text) + .setButton2Icon(R.drawable.ic_settings_delete) + .setButton2OnClickListener(new UninstallAndDisableButtonListener()) + .setButton3Text(R.string.force_stop) + .setButton3Icon(R.drawable.ic_settings_force_stop) + .setButton3OnClickListener(new ForceStopButtonListener()) + .setButton3Enabled(false); + } + private void startListeningToPackageRemove() { if (mListeningToPackageRemove) { return; diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/AppButtonsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppButtonsPreferenceControllerTest.java index 7304fc20d9f..c2bcd1c7793 100644 --- a/tests/robotests/src/com/android/settings/applications/appinfo/AppButtonsPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/applications/appinfo/AppButtonsPreferenceControllerTest.java @@ -50,6 +50,8 @@ import android.os.UserManager; import android.util.ArraySet; import android.view.View; +import androidx.preference.PreferenceScreen; + import com.android.settings.R; import com.android.settings.SettingsActivity; import com.android.settings.core.InstrumentedPreferenceFragment; @@ -116,6 +118,8 @@ public class AppButtonsPreferenceControllerTest { private UserManager mUserManager; @Mock private PackageInfo mPackageInfo; + @Mock + private PreferenceScreen mScreen; private Context mContext; private Intent mUninstallIntent; @@ -471,6 +475,20 @@ public class AppButtonsPreferenceControllerTest { assertThat(mController.refreshUi()).isFalse(); } + @Test + public void refreshUi_buttonPreferenceNull_shouldNotCrash() + throws PackageManager.NameNotFoundException { + doReturn(AppButtonsPreferenceController.AVAILABLE) + .when(mController).getAvailabilityStatus(); + doReturn(mPackageInfo).when(mPackageManger).getPackageInfo(anyString(), anyInt()); + doReturn(mButtonPrefs).when(mScreen).findPreference(anyString()); + mController.displayPreference(mScreen); + mController.mButtonsPref = null; + + // Should not crash in this method + assertThat(mController.refreshUi()).isTrue(); + } + @Test public void onPackageListChanged_available_shouldRefreshUi() { doReturn(AppButtonsPreferenceController.AVAILABLE) @@ -545,11 +563,19 @@ public class AppButtonsPreferenceControllerTest { private ActionButtonsPreference createMock() { final ActionButtonsPreference pref = mock(ActionButtonsPreference.class); + when(pref.setButton1Text(anyInt())).thenReturn(pref); + when(pref.setButton1Icon(anyInt())).thenReturn(pref); + when(pref.setButton1Enabled(anyBoolean())).thenReturn(pref); + when(pref.setButton1OnClickListener(any(View.OnClickListener.class))).thenReturn(pref); when(pref.setButton2Text(anyInt())).thenReturn(pref); when(pref.setButton2Icon(anyInt())).thenReturn(pref); when(pref.setButton2Enabled(anyBoolean())).thenReturn(pref); when(pref.setButton2Visible(anyBoolean())).thenReturn(pref); when(pref.setButton2OnClickListener(any(View.OnClickListener.class))).thenReturn(pref); + when(pref.setButton3Text(anyInt())).thenReturn(pref); + when(pref.setButton3Icon(anyInt())).thenReturn(pref); + when(pref.setButton3Enabled(anyBoolean())).thenReturn(pref); + when(pref.setButton3OnClickListener(any(View.OnClickListener.class))).thenReturn(pref); return pref; }