1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.settings.development; 17 18 import android.content.ContentResolver; 19 import android.content.Context; 20 import android.content.Intent; 21 import android.content.pm.PackageManager; 22 import android.content.pm.ResolveInfo; 23 import android.os.UserHandle; 24 import android.os.UserManager; 25 import android.provider.Settings; 26 27 import androidx.annotation.VisibleForTesting; 28 import androidx.preference.Preference; 29 30 import com.android.settings.core.PreferenceControllerMixin; 31 import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; 32 import com.android.settingslib.RestrictedLockUtilsInternal; 33 import com.android.settingslib.RestrictedSwitchPreference; 34 import com.android.settingslib.development.DeveloperOptionsPreferenceController; 35 36 import java.util.List; 37 38 /** 39 * Controller to manage the state of "Verify apps over USB" toggle. 40 */ 41 public class VerifyAppsOverUsbPreferenceController extends DeveloperOptionsPreferenceController 42 implements Preference.OnPreferenceChangeListener, AdbOnChangeListener, 43 PreferenceControllerMixin { 44 private static final String VERIFY_APPS_OVER_USB_KEY = "verify_apps_over_usb"; 45 private static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive"; 46 47 @VisibleForTesting 48 static final int SETTING_VALUE_ON = 1; 49 @VisibleForTesting 50 static final int SETTING_VALUE_OFF = 0; 51 52 /** 53 * Class for indirection of RestrictedLockUtils for testing purposes. It would be nice to mock 54 * the appropriate methods in UserManager instead but they aren't accessible. 55 */ 56 @VisibleForTesting 57 class RestrictedLockUtilsDelegate { checkIfRestrictionEnforced( Context context, String userRestriction, int userId)58 public EnforcedAdmin checkIfRestrictionEnforced( 59 Context context, String userRestriction, int userId) { 60 return RestrictedLockUtilsInternal.checkIfRestrictionEnforced(context, userRestriction, 61 userId); 62 } 63 } 64 65 // NB: This field is accessed using reflection in the test, please keep name in sync. 66 private final RestrictedLockUtilsDelegate mRestrictedLockUtils = 67 new RestrictedLockUtilsDelegate(); 68 69 // This field is accessed using reflection in the test, please keep name in sync. 70 private final PackageManager mPackageManager; 71 VerifyAppsOverUsbPreferenceController(Context context)72 public VerifyAppsOverUsbPreferenceController(Context context) { 73 super(context); 74 75 mPackageManager = context.getPackageManager(); 76 } 77 78 @Override isAvailable()79 public boolean isAvailable() { 80 return Settings.Global.getInt(mContext.getContentResolver(), 81 Settings.Global.PACKAGE_VERIFIER_SETTING_VISIBLE, 1 /* default */) > 0; 82 } 83 84 @Override getPreferenceKey()85 public String getPreferenceKey() { 86 return VERIFY_APPS_OVER_USB_KEY; 87 } 88 89 @Override onPreferenceChange(Preference preference, Object newValue)90 public boolean onPreferenceChange(Preference preference, Object newValue) { 91 final boolean isEnabled = (Boolean) newValue; 92 Settings.Global.putInt(mContext.getContentResolver(), 93 Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB, 94 isEnabled ? SETTING_VALUE_ON : SETTING_VALUE_OFF); 95 return true; 96 } 97 98 @Override updateState(Preference preference)99 public void updateState(Preference preference) { 100 final RestrictedSwitchPreference restrictedPreference = 101 (RestrictedSwitchPreference) preference; 102 if (!shouldBeEnabled()) { 103 restrictedPreference.setChecked(false); 104 restrictedPreference.setDisabledByAdmin(null); 105 restrictedPreference.setEnabled(false); 106 return; 107 } 108 109 final EnforcedAdmin enforcingAdmin = mRestrictedLockUtils.checkIfRestrictionEnforced( 110 mContext, UserManager.ENSURE_VERIFY_APPS, UserHandle.myUserId()); 111 if (enforcingAdmin != null) { 112 restrictedPreference.setChecked(true); 113 restrictedPreference.setDisabledByAdmin(enforcingAdmin); 114 return; 115 } 116 117 restrictedPreference.setEnabled(true); 118 final boolean checked = Settings.Global.getInt(mContext.getContentResolver(), 119 Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB, SETTING_VALUE_ON) 120 != SETTING_VALUE_OFF; 121 restrictedPreference.setChecked(checked); 122 } 123 124 @Override onAdbSettingChanged()125 public void onAdbSettingChanged() { 126 if (isAvailable()) { 127 updateState(mPreference); 128 } 129 } 130 131 @Override onDeveloperOptionsSwitchEnabled()132 protected void onDeveloperOptionsSwitchEnabled() { 133 super.onDeveloperOptionsSwitchEnabled(); 134 updateState(mPreference); 135 } 136 137 /** 138 * Checks whether the toggle should be enabled depending on whether verify apps over USB is 139 * possible currently. If ADB is disabled or if package verifier does not exist, the toggle 140 * should be disabled. 141 */ shouldBeEnabled()142 private boolean shouldBeEnabled() { 143 final ContentResolver cr = mContext.getContentResolver(); 144 if (Settings.Global.getInt(cr, Settings.Global.ADB_ENABLED, 145 AdbPreferenceController.ADB_SETTING_OFF) 146 == AdbPreferenceController.ADB_SETTING_OFF) { 147 return false; 148 } 149 final Intent verification = new Intent(Intent.ACTION_PACKAGE_NEEDS_VERIFICATION); 150 verification.setType(PACKAGE_MIME_TYPE); 151 verification.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 152 final List<ResolveInfo> receivers = mPackageManager.queryBroadcastReceivers( 153 verification, 0 /* flags */); 154 return !receivers.isEmpty(); 155 } 156 } 157