diff --git a/src/com/android/settings/deviceinfo/SecurityPatchPreferenceController.java b/src/com/android/settings/deviceinfo/SecurityPatchPreferenceController.java index f38602ab531..9b0120e7e77 100644 --- a/src/com/android/settings/deviceinfo/SecurityPatchPreferenceController.java +++ b/src/com/android/settings/deviceinfo/SecurityPatchPreferenceController.java @@ -23,9 +23,14 @@ import android.text.TextUtils; import android.util.Log; import com.android.settings.core.PreferenceControllerMixin; +import com.android.settings.deviceinfo.firmwareversion.SecurityPatchLevelDialogController; import com.android.settingslib.DeviceInfoUtils; import com.android.settingslib.core.AbstractPreferenceController; +/** + * deprecated in favor of {@link SecurityPatchLevelDialogController} + */ +@Deprecated public class SecurityPatchPreferenceController extends AbstractPreferenceController implements PreferenceControllerMixin { diff --git a/src/com/android/settings/deviceinfo/firmwareversion/FirmwareVersionDialogFragment.java b/src/com/android/settings/deviceinfo/firmwareversion/FirmwareVersionDialogFragment.java index 4ef4e05493c..265bee55a7d 100644 --- a/src/com/android/settings/deviceinfo/firmwareversion/FirmwareVersionDialogFragment.java +++ b/src/com/android/settings/deviceinfo/firmwareversion/FirmwareVersionDialogFragment.java @@ -69,6 +69,13 @@ public class FirmwareVersionDialogFragment extends InstrumentedDialogFragment { } } + public void removeSettingFromScreen(int viewId) { + final View view = mRootView.findViewById(viewId); + if (view != null) { + view.setVisibility(View.GONE); + } + } + public void registerClickListener(int viewId, View.OnClickListener listener) { final View view = mRootView.findViewById(viewId); if (view != null) { @@ -78,5 +85,6 @@ public class FirmwareVersionDialogFragment extends InstrumentedDialogFragment { private void initializeControllers() { new FirmwareVersionDialogController(this).initialize(); + new SecurityPatchLevelDialogController(this).initialize(); } } diff --git a/src/com/android/settings/deviceinfo/firmwareversion/SecurityPatchLevelDialogController.java b/src/com/android/settings/deviceinfo/firmwareversion/SecurityPatchLevelDialogController.java new file mode 100644 index 00000000000..01f440daca0 --- /dev/null +++ b/src/com/android/settings/deviceinfo/firmwareversion/SecurityPatchLevelDialogController.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2017 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.deviceinfo.firmwareversion; + +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.support.annotation.VisibleForTesting; +import android.text.TextUtils; +import android.util.Log; +import android.view.View; + +import com.android.settings.R; +import com.android.settingslib.DeviceInfoUtils; +import com.android.settingslib.wrapper.PackageManagerWrapper; + +public class SecurityPatchLevelDialogController implements View.OnClickListener { + + private static final String TAG = "SecurityPatchCtrl"; + private static final Uri INTENT_URI_DATA = Uri.parse( + "https://source.android.com/security/bulletin/"); + + @VisibleForTesting + static final int SECURITY_PATCH_VALUE_ID = R.id.security_patch_level_value; + @VisibleForTesting + static final int SECURITY_PATCH_LABEL_ID = R.id.security_patch_level_label; + + private final FirmwareVersionDialogFragment mDialog; + private final Context mContext; + private final PackageManagerWrapper mPackageManager; + private final String mCurrentPatch; + + public SecurityPatchLevelDialogController(FirmwareVersionDialogFragment dialog) { + mDialog = dialog; + mContext = dialog.getContext(); + mPackageManager = new PackageManagerWrapper(mContext.getPackageManager()); + mCurrentPatch = DeviceInfoUtils.getSecurityPatch(); + } + + @Override + public void onClick(View v) { + final Intent intent = new Intent(); + intent.setAction(Intent.ACTION_VIEW); + intent.setData(INTENT_URI_DATA); + if (mPackageManager.queryIntentActivities(intent, 0).isEmpty()) { + // Don't send out the intent to stop crash + Log.w(TAG, "Stop click action on " + SECURITY_PATCH_VALUE_ID + ": " + + "queryIntentActivities() returns empty"); + return; + } + + mContext.startActivity(intent); + } + + /** + * Populates the security patch level field in the dialog and registers click listeners. + */ + public void initialize() { + if (TextUtils.isEmpty(mCurrentPatch)) { + mDialog.removeSettingFromScreen(SECURITY_PATCH_LABEL_ID); + mDialog.removeSettingFromScreen(SECURITY_PATCH_VALUE_ID); + return; + } + registerListeners(); + mDialog.setText(SECURITY_PATCH_VALUE_ID, mCurrentPatch); + } + + private void registerListeners() { + mDialog.registerClickListener(SECURITY_PATCH_LABEL_ID, this /* listener */); + mDialog.registerClickListener(SECURITY_PATCH_VALUE_ID, this /* listener */); + } +} diff --git a/tests/robotests/src/com/android/settings/deviceinfo/firmwareversion/SecurityPatchLevelDialogControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/firmwareversion/SecurityPatchLevelDialogControllerTest.java new file mode 100644 index 00000000000..ea37c2ed4e6 --- /dev/null +++ b/tests/robotests/src/com/android/settings/deviceinfo/firmwareversion/SecurityPatchLevelDialogControllerTest.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2017 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.deviceinfo.firmwareversion; + +import static com.android.settings.deviceinfo.firmwareversion.SecurityPatchLevelDialogController + .SECURITY_PATCH_LABEL_ID; +import static com.android.settings.deviceinfo.firmwareversion.SecurityPatchLevelDialogController + .SECURITY_PATCH_VALUE_ID; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.os.Build; +import android.view.View; + +import com.android.settings.TestConfig; +import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settingslib.wrapper.PackageManagerWrapper; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; +import org.robolectric.util.ReflectionHelpers; + +import java.util.Collections; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O) +public class SecurityPatchLevelDialogControllerTest { + + @Mock + private PackageManagerWrapper mPackageManager; + @Mock + private FirmwareVersionDialogFragment mDialog; + @Mock + private View mView; + + private Context mContext; + private SecurityPatchLevelDialogController mController; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + mContext = spy(RuntimeEnvironment.application); + when(mDialog.getContext()).thenReturn(mContext); + } + + @Test + public void initialize_noPatchInfo_shouldRemoveSettingFromDialog() { + ReflectionHelpers.setStaticField(Build.VERSION.class, "SECURITY_PATCH", ""); + mController = new SecurityPatchLevelDialogController(mDialog); + + mController.initialize(); + + verify(mDialog).removeSettingFromScreen(SECURITY_PATCH_VALUE_ID); + verify(mDialog).removeSettingFromScreen(SECURITY_PATCH_LABEL_ID); + } + + @Test + public void initialize_patchInfoAvailable_shouldRegisterListeners() { + ReflectionHelpers.setStaticField(Build.VERSION.class, "SECURITY_PATCH", "foobar"); + mController = new SecurityPatchLevelDialogController(mDialog); + + mController.initialize(); + + verify(mDialog).registerClickListener(eq(SECURITY_PATCH_LABEL_ID), any()); + verify(mDialog).registerClickListener(eq(SECURITY_PATCH_VALUE_ID), any()); + } + + @Test + public void onClick_noActivityIntent_shouldDoNothing() { + when(mPackageManager.queryIntentActivities(any(), anyInt())).thenReturn( + Collections.emptyList()); + mController = new SecurityPatchLevelDialogController(mDialog); + ReflectionHelpers.setField(mController, "mPackageManager", mPackageManager); + + mController.onClick(mView); + + verify(mContext, never()).startActivity(any()); + } + + @Test + public void onClick_activityIntentFound_shouldStartActivity() { + when(mPackageManager.queryIntentActivities(any(), anyInt())).thenReturn( + Collections.singletonList(null)); + mController = new SecurityPatchLevelDialogController(mDialog); + ReflectionHelpers.setField(mController, "mPackageManager", mPackageManager); + + mController.onClick(mView); + + verify(mContext).startActivity(any()); + } +}