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.ResolveInfo; 22 import android.os.UserHandle; 23 import android.os.UserManager; 24 import android.provider.Settings; 25 import android.support.annotation.VisibleForTesting; 26 import android.support.v7.preference.Preference; 27 28 import com.android.settings.core.PreferenceControllerMixin; 29 import com.android.settingslib.RestrictedLockUtils; 30 import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; 31 import com.android.settingslib.RestrictedSwitchPreference; 32 import com.android.settingslib.development.DeveloperOptionsPreferenceController; 33 import com.android.settingslib.wrapper.PackageManagerWrapper; 34 35 import java.util.List; 36 37 /** 38 * Controller to manage the state of "Verify apps over USB" toggle. 39 */ 40 public class VerifyAppsOverUsbPreferenceController extends DeveloperOptionsPreferenceController 41 implements Preference.OnPreferenceChangeListener, AdbOnChangeListener, 42 PreferenceControllerMixin { 43 private static final String VERIFY_APPS_OVER_USB_KEY = "verify_apps_over_usb"; 44 private static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive"; 45 46 @VisibleForTesting 47 static final int SETTING_VALUE_ON = 1; 48 @VisibleForTesting 49 static final int SETTING_VALUE_OFF = 0; 50 51 /** 52 * Class for indirection of RestrictedLockUtils for testing purposes. It would be nice to mock 53 * the appropriate methods in UserManager instead but they aren't accessible. 54 */ 55 @VisibleForTesting 56 class RestrictedLockUtilsDelegate { 57 public EnforcedAdmin checkIfRestrictionEnforced( 58 Context context, String userRestriction, int userId) { 59 return RestrictedLockUtils.checkIfRestrictionEnforced(context, userRestriction, userId); 60 } 61 } 62 63 // NB: This field is accessed using reflection in the test, please keep name in sync. 64 private final RestrictedLockUtilsDelegate mRestrictedLockUtils = 65 new RestrictedLockUtilsDelegate(); 66 67 // This field is accessed using reflection in the test, please keep name in sync. 68 private final PackageManagerWrapper mPackageManager; 69 70 public VerifyAppsOverUsbPreferenceController(Context context) { 71 super(context); 72 73 mPackageManager = new PackageManagerWrapper(context.getPackageManager()); 74 } 75 76 @Override 77 public boolean isAvailable() { 78 return Settings.Global.getInt(mContext.getContentResolver(), 79 Settings.Global.PACKAGE_VERIFIER_SETTING_VISIBLE, 1 /* default */) > 0; 80 } 81 82 @Override 83 public String getPreferenceKey() { 84 return VERIFY_APPS_OVER_USB_KEY; 85 } 86 87 @Override 88 public boolean onPreferenceChange(Preference preference, Object newValue) { 89 final boolean isEnabled = (Boolean) newValue; 90 Settings.Global.putInt(mContext.getContentResolver(), 91 Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB, 92 isEnabled ? SETTING_VALUE_ON : SETTING_VALUE_OFF); 93 return true; 94 } 95 96 @Override 97 public void updateState(Preference preference) { 98 final RestrictedSwitchPreference restrictedPreference = 99 (RestrictedSwitchPreference) preference; 100 if (!shouldBeEnabled()) { 101 restrictedPreference.setChecked(false); 102 restrictedPreference.setDisabledByAdmin(null); 103 restrictedPreference.setEnabled(false); 104 return; 105 } 106 107 final EnforcedAdmin enforcingAdmin = mRestrictedLockUtils.checkIfRestrictionEnforced( 108 mContext, UserManager.ENSURE_VERIFY_APPS, UserHandle.myUserId()); 109 if (enforcingAdmin != null) { 110 restrictedPreference.setChecked(true); 111 restrictedPreference.setDisabledByAdmin(enforcingAdmin); 112 return; 113 } 114 115 restrictedPreference.setEnabled(true); 116 final boolean checked = Settings.Global.getInt(mContext.getContentResolver(), 117 Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB, SETTING_VALUE_ON) 118 != SETTING_VALUE_OFF; 119 restrictedPreference.setChecked(checked); 120 } 121 122 @Override 123 public void onAdbSettingChanged() { 124 if (isAvailable()) { 125 updateState(mPreference); 126 } 127 } 128 129 @Override 130 protected void onDeveloperOptionsSwitchEnabled() { 131 super.onDeveloperOptionsSwitchEnabled(); 132 updateState(mPreference); 133 } 134 135 /** 136 * Checks whether the toggle should be enabled depending on whether verify apps over USB is 137 * possible currently. If ADB is disabled or if package verifier does not exist, the toggle 138 * should be disabled. 139 */ 140 private boolean shouldBeEnabled() { 141 final ContentResolver cr = mContext.getContentResolver(); 142 if (Settings.Global.getInt(cr, Settings.Global.ADB_ENABLED, 143 AdbPreferenceController.ADB_SETTING_OFF) 144 == AdbPreferenceController.ADB_SETTING_OFF) { 145 return false; 146 } 147 if (Settings.Global.getInt(cr, Settings.Global.PACKAGE_VERIFIER_ENABLE, SETTING_VALUE_ON) 148 == SETTING_VALUE_OFF) { 149 return false; 150 } else { 151 final Intent verification = new Intent(Intent.ACTION_PACKAGE_NEEDS_VERIFICATION); 152 verification.setType(PACKAGE_MIME_TYPE); 153 verification.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 154 final List<ResolveInfo> receivers = mPackageManager.queryBroadcastReceivers( 155 verification, 0 /* flags */); 156 if (receivers.size() == 0) { 157 return false; 158 } 159 } 160 return true; 161 } 162 } 163