Merge "Update subtext for Security settings." into oc-dev
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index b38cea7..02351e5 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -2645,6 +2645,10 @@
android:value="true" />
</activity>
+ <!-- Confirmation dialog for enabling notification access from CompanionDeviceManager -->
+ <activity android:name=".notification.NotificationAccessConfirmationActivity"
+ android:theme="@android:style/Theme.DeviceDefault.Light.Dialog.Alert" />
+
<receiver android:name=".widget.SettingsAppWidgetProvider"
android:label="@string/gadget_title"
android:exported="false"
diff --git a/res/xml/app_storage_settings.xml b/res/xml/app_storage_settings.xml
index 6bd8ae3..1c71bfa 100644
--- a/res/xml/app_storage_settings.xml
+++ b/res/xml/app_storage_settings.xml
@@ -15,8 +15,14 @@
-->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
android:title="@string/application_info_label">
+ <com.android.settings.applications.LayoutPreference
+ android:key="header_view"
+ android:layout="@layout/app_action_buttons"
+ android:selectable="false" />
+
<com.android.settings.applications.SpacePreference
android:key="storage_space"
android:layout_height="8dp" />
@@ -36,13 +42,9 @@
<PreferenceCategory
android:key="storage_category"
android:layout="@layout/tall_preference_category"
- android:title="@string/storage_label">
-
- <Preference
- android:key="total_size"
- android:title="@string/total_size_label"
- android:selectable="false"
- android:layout="@layout/horizontal_preference" />
+ android:title="@string/app_info_storage_title"
+ settings:allowDividerAbove="false"
+ settings:allowDividerBelow="false">
<Preference
android:key="app_size"
@@ -56,32 +58,28 @@
android:selectable="false"
android:layout="@layout/horizontal_preference" />
- <com.android.settings.applications.LayoutPreference
- android:key="clear_data_button"
+ <Preference
+ android:key="cache_size"
+ android:title="@string/cache_size_label"
android:selectable="false"
- android:layout="@layout/single_button_panel" />
+ android:layout="@layout/horizontal_preference" />
+
+ <Preference
+ android:key="total_size"
+ android:title="@string/total_size_label"
+ android:selectable="false"
+ android:layout="@layout/horizontal_preference" />
+
+ <com.android.settings.applications.SpacePreference
+ android:layout_height="8dp" />
+
</PreferenceCategory>
- <com.android.settings.applications.SpacePreference
- android:layout_height="8dp" />
-
- <Preference
- android:key="cache_size"
- android:title="@string/cache_size_label"
- android:selectable="false"
- android:layout="@layout/horizontal_preference" />
-
- <com.android.settings.applications.LayoutPreference
- android:key="clear_cache_button"
- android:selectable="false"
- android:layout="@layout/single_button_panel" />
-
- <com.android.settings.applications.SpacePreference
- android:layout_height="8dp" />
-
<PreferenceCategory
android:key="uri_category"
- android:layout="@layout/headerless_preference_category" >
+ android:layout="@layout/headerless_preference_category"
+ settings:allowDividerAbove="false"
+ settings:allowDividerBelow="false">
<com.android.settings.applications.LayoutPreference
android:key="clear_uri_button"
android:layout="@layout/single_button_panel"
diff --git a/src/com/android/settings/ChooseLockGeneric.java b/src/com/android/settings/ChooseLockGeneric.java
index 3e9304d..546b11e 100644
--- a/src/com/android/settings/ChooseLockGeneric.java
+++ b/src/com/android/settings/ChooseLockGeneric.java
@@ -129,6 +129,7 @@
private boolean mHideDrawer = false;
private ManagedLockPasswordProvider mManagedPasswordProvider;
private boolean mIsSetNewPassword = false;
+ private UserManager mUserManager;
protected boolean mForFingerprint = false;
@@ -166,6 +167,7 @@
ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, false);
mForChangeCredRequiredForBoot = getArguments() != null && getArguments().getBoolean(
ChooseLockSettingsHelper.EXTRA_KEY_FOR_CHANGE_CRED_REQUIRED_FOR_BOOT);
+ mUserManager = UserManager.get(getActivity());
if (savedInstanceState != null) {
mPasswordConfirmed = savedInstanceState.getBoolean(PASSWORD_CONFIRMED);
@@ -751,11 +753,10 @@
if (mFingerprintManager != null && mFingerprintManager.isHardwareDetected()) {
mFingerprintManager.setActiveUser(UserHandle.myUserId());
}
- final UserManager um = UserManager.get(getActivity());
boolean hasChildProfile = false;
- if (!um.getUserInfo(parentUserId).isManagedProfile()) {
+ if (!mUserManager.getUserInfo(parentUserId).isManagedProfile()) {
// Current user is primary profile, remove work profile fingerprints if necessary
- final List<UserInfo> profiles = um.getProfiles(parentUserId);
+ final List<UserInfo> profiles = mUserManager.getProfiles(parentUserId);
final int profilesSize = profiles.size();
for (int i = 0; i < profilesSize; i++) {
final UserInfo userInfo = profiles.get(i);
diff --git a/src/com/android/settings/applications/AppStorageSettings.java b/src/com/android/settings/applications/AppStorageSettings.java
index 8d41558..0676f56 100644
--- a/src/com/android/settings/applications/AppStorageSettings.java
+++ b/src/com/android/settings/applications/AppStorageSettings.java
@@ -90,13 +90,10 @@
private static final String KEY_TOTAL_SIZE = "total_size";
private static final String KEY_APP_SIZE = "app_size";
- private static final String KEY_EXTERNAL_CODE_SIZE = "external_code_size";
private static final String KEY_DATA_SIZE = "data_size";
- private static final String KEY_EXTERNAL_DATA_SIZE = "external_data_size";
private static final String KEY_CACHE_SIZE = "cache_size";
- private static final String KEY_CLEAR_DATA = "clear_data_button";
- private static final String KEY_CLEAR_CACHE = "clear_cache_button";
+ private static final String KEY_HEADER_BUTTONS = "header_view";
private static final String KEY_URI_CATEGORY = "uri_category";
private static final String KEY_CLEAR_URI = "clear_uri_button";
@@ -119,16 +116,11 @@
private boolean mCanClearData = true;
private boolean mCacheCleared;
- private AppStorageStats mLastResult;
private AppStorageSizesController mSizeController;
private ClearCacheObserver mClearCacheObserver;
private ClearUserDataObserver mClearDataObserver;
- // Resource strings
- private CharSequence mInvalidSizeStr;
- private CharSequence mComputingStr;
-
private VolumeInfo[] mCandidates;
private AlertDialog.Builder mDialogBuilder;
private ApplicationInfo mInfo;
@@ -158,9 +150,6 @@
}
private void setupViews() {
- mComputingStr = getActivity().getText(R.string.computing_size);
- mInvalidSizeStr = getActivity().getText(R.string.invalid_size_value);
-
// Set default values on sizes
mSizeController = new AppStorageSizesController.Builder()
.setTotalSizePreference(findPreference(KEY_TOTAL_SIZE))
@@ -171,8 +160,8 @@
.setErrorString(R.string.invalid_size_value)
.build();
- mClearDataButton = (Button) ((LayoutPreference) findPreference(KEY_CLEAR_DATA))
- .findViewById(R.id.button);
+ mClearDataButton = (Button) ((LayoutPreference) findPreference(KEY_HEADER_BUTTONS))
+ .findViewById(R.id.left_button);
mStorageUsed = findPreference(KEY_STORAGE_USED);
mChangeStorageButton = (Button) ((LayoutPreference) findPreference(KEY_CHANGE_STORAGE))
@@ -182,8 +171,8 @@
// Cache section
mCacheSize = findPreference(KEY_CACHE_SIZE);
- mClearCacheButton = (Button) ((LayoutPreference) findPreference(KEY_CLEAR_CACHE))
- .findViewById(R.id.button);
+ mClearCacheButton = (Button) ((LayoutPreference) findPreference(KEY_HEADER_BUTTONS))
+ .findViewById(R.id.right_button);
mClearCacheButton.setText(R.string.clear_cache_btn_text);
// URI permissions section
@@ -267,7 +256,7 @@
if (mAppEntry == null) {
return false;
}
- updateUiWithSize(mLastResult);
+ updateUiWithSize(mSizeController.getLastResult());
refreshGrantedUriPermissions();
final VolumeInfo currentVol = getActivity().getPackageManager()
diff --git a/src/com/android/settings/applications/AppStorageSizesController.java b/src/com/android/settings/applications/AppStorageSizesController.java
index 94935bd..23a3eb2 100644
--- a/src/com/android/settings/applications/AppStorageSizesController.java
+++ b/src/com/android/settings/applications/AppStorageSizesController.java
@@ -110,6 +110,13 @@
mCachedCleared = isCleared;
}
+ /**
+ * Returns the last result calculated, if it exists. If it does not, returns null.
+ */
+ public StorageStatsSource.AppStorageStats getLastResult() {
+ return mLastResult;
+ }
+
private String getSizeStr(Context context, long size) {
return Formatter.formatFileSize(context, size);
}
diff --git a/src/com/android/settings/applications/assist/DefaultAssistPreferenceController.java b/src/com/android/settings/applications/assist/DefaultAssistPreferenceController.java
index 747ce4d..b8d6a87 100644
--- a/src/com/android/settings/applications/assist/DefaultAssistPreferenceController.java
+++ b/src/com/android/settings/applications/assist/DefaultAssistPreferenceController.java
@@ -24,6 +24,7 @@
import android.service.voice.VoiceInteractionService;
import android.service.voice.VoiceInteractionServiceInfo;
+import android.support.annotation.VisibleForTesting;
import com.android.internal.app.AssistUtils;
import com.android.settings.applications.defaultapps.DefaultAppInfo;
import com.android.settings.applications.defaultapps.DefaultAppPreferenceController;
@@ -56,13 +57,10 @@
if (services == null || services.isEmpty()) {
return null;
}
- final ResolveInfo resolveInfo = services.get(0);
- final VoiceInteractionServiceInfo voiceInfo =
- new VoiceInteractionServiceInfo(pm, resolveInfo.serviceInfo);
- if (!voiceInfo.getSupportsAssist()) {
+ final String activity = getAssistSettingsActivity(cn, services.get(0), pm);
+ if (activity == null) {
return null;
}
- final String activity = voiceInfo.getSettingsActivity();
return new Intent(Intent.ACTION_MAIN)
.setComponent(new ComponentName(cn.getPackageName(), activity));
}
@@ -85,4 +83,14 @@
}
return new DefaultAppInfo(mPackageManager, mUserId, cn);
}
+
+ @VisibleForTesting
+ String getAssistSettingsActivity(ComponentName cn, ResolveInfo resolveInfo, PackageManager pm) {
+ final VoiceInteractionServiceInfo voiceInfo =
+ new VoiceInteractionServiceInfo(pm, resolveInfo.serviceInfo);
+ if (!voiceInfo.getSupportsAssist()) {
+ return null;
+ }
+ return voiceInfo.getSettingsActivity();
+ }
}
diff --git a/src/com/android/settings/deviceinfo/StorageDashboardFragment.java b/src/com/android/settings/deviceinfo/StorageDashboardFragment.java
index 602e65f..bc64ffa 100644
--- a/src/com/android/settings/deviceinfo/StorageDashboardFragment.java
+++ b/src/com/android/settings/deviceinfo/StorageDashboardFragment.java
@@ -117,7 +117,7 @@
@Override
public void onResume() {
super.onResume();
- getLoaderManager().initLoader(STORAGE_JOB_ID, Bundle.EMPTY, this);
+ getLoaderManager().restartLoader(STORAGE_JOB_ID, Bundle.EMPTY, this);
getLoaderManager().initLoader(ICON_JOB_ID, Bundle.EMPTY, new IconLoaderCallbacks());
}
diff --git a/src/com/android/settings/deviceinfo/storage/StorageAsyncLoader.java b/src/com/android/settings/deviceinfo/storage/StorageAsyncLoader.java
index e83c5d2..cf0239d 100644
--- a/src/com/android/settings/deviceinfo/storage/StorageAsyncLoader.java
+++ b/src/com/android/settings/deviceinfo/storage/StorageAsyncLoader.java
@@ -79,8 +79,15 @@
UserHandle myUser = UserHandle.of(userId);
for (int i = 0, size = applicationInfos.size(); i < size; i++) {
ApplicationInfo app = applicationInfos.get(i);
- StorageStatsSource.AppStorageStats stats =
- mStatsManager.getStatsForPackage(mUuid, app.packageName, myUser);
+
+ StorageStatsSource.AppStorageStats stats;
+ try {
+ stats = mStatsManager.getStatsForPackage(mUuid, app.packageName, myUser);
+ } catch (IllegalStateException e) {
+ // This may happen if the package was removed during our calculation.
+ Log.w("App unexpectedly not found", e);
+ continue;
+ }
long attributedAppSizeInBytes = stats.getDataBytes();
// This matches how the package manager calculates sizes -- by zeroing out code sizes of
diff --git a/src/com/android/settings/notification/NotificationAccessConfirmationActivity.java b/src/com/android/settings/notification/NotificationAccessConfirmationActivity.java
new file mode 100644
index 0000000..a78fed7
--- /dev/null
+++ b/src/com/android/settings/notification/NotificationAccessConfirmationActivity.java
@@ -0,0 +1,137 @@
+/*
+ * 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.notification;
+
+import static com.android.internal.notification.NotificationAccessConfirmationActivityContract
+ .EXTRA_COMPONENT_NAME;
+import static com.android.internal.notification.NotificationAccessConfirmationActivityContract
+ .EXTRA_PACKAGE_TITLE;
+import static com.android.internal.notification.NotificationAccessConfirmationActivityContract
+ .EXTRA_USER_ID;
+
+import android.Manifest;
+import android.annotation.Nullable;
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.DialogInterface;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.provider.SettingsStringUtil;
+import android.util.Slog;
+import android.view.accessibility.AccessibilityEvent;
+
+import com.android.internal.app.AlertActivity;
+import com.android.internal.app.AlertController;
+import com.android.settings.R;
+import com.android.settings.core.TouchOverlayManager;
+
+/** @hide */
+public class NotificationAccessConfirmationActivity extends Activity
+ implements DialogInterface {
+
+ private static final boolean DEBUG = false;
+ private static final String LOG_TAG = "NotificationAccessConfirmationActivity";
+
+ private int mUserId;
+ private ComponentName mComponentName;
+ private TouchOverlayManager mTouchOverlayManager;
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mTouchOverlayManager = new TouchOverlayManager(this);
+
+ mComponentName = getIntent().getParcelableExtra(EXTRA_COMPONENT_NAME);
+ mUserId = getIntent().getIntExtra(EXTRA_USER_ID, UserHandle.USER_NULL);
+ String pkgTitle = getIntent().getStringExtra(EXTRA_PACKAGE_TITLE);
+
+ AlertController.AlertParams p = new AlertController.AlertParams(this);
+ p.mTitle = getString(
+ R.string.notification_listener_security_warning_title,
+ pkgTitle);
+ p.mMessage = getString(
+ R.string.notification_listener_security_warning_summary,
+ pkgTitle);
+ p.mPositiveButtonText = getString(R.string.allow);
+ p.mPositiveButtonListener = (a, b) -> onAllow();
+ p.mNegativeButtonText = getString(R.string.deny);
+ p.mNegativeButtonListener = (a, b) -> cancel();
+ AlertController
+ .create(this, this, getWindow())
+ .installContent(p);
+ }
+
+ private void onAllow() {
+ String requiredPermission = Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE;
+ try {
+ ServiceInfo serviceInfo = getPackageManager().getServiceInfo(mComponentName, 0);
+ if (!requiredPermission.equals(serviceInfo.permission)) {
+ Slog.e(LOG_TAG,
+ "Service " + mComponentName + " lacks permission " + requiredPermission);
+ return;
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.e(LOG_TAG, "Failed to get service info for " + mComponentName, e);
+ return;
+ }
+
+ final SettingsStringUtil.SettingStringHelper setting =
+ new SettingsStringUtil.SettingStringHelper(
+ getContentResolver(),
+ Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
+ mUserId);
+ setting.write(SettingsStringUtil.ComponentNameSet.add(setting.read(), mComponentName));
+
+ finish();
+ }
+
+ @Override
+ public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
+ return AlertActivity.dispatchPopulateAccessibilityEvent(this, event);
+ }
+
+ @Override
+ public void cancel() {
+ finish();
+ }
+
+ @Override
+ public void dismiss() {
+ // This is called after the click, since we finish when handling the
+ // click, don't do that again here.
+ if (!isFinishing()) {
+ finish();
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ mTouchOverlayManager.setOverlayAllowed(false);
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ mTouchOverlayManager.setOverlayAllowed(true);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/applications/assist/DefaultAssistPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/assist/DefaultAssistPreferenceControllerTest.java
index 1182762..728a8a5 100644
--- a/tests/robotests/src/com/android/settings/applications/assist/DefaultAssistPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/applications/assist/DefaultAssistPreferenceControllerTest.java
@@ -16,7 +16,16 @@
package com.android.settings.applications.assist;
+import android.Manifest;
+import android.app.SearchManager;
+import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
import android.provider.Settings;
import com.android.settings.SettingsRobolectricTestRunner;
@@ -24,6 +33,8 @@
import com.android.settings.applications.defaultapps.DefaultAppInfo;
import com.android.settings.testutils.shadow.ShadowSecureSettings;
+import java.util.ArrayList;
+import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -32,6 +43,13 @@
import org.robolectric.annotation.Config;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
@@ -39,6 +57,10 @@
@Mock
private Context mContext;
+ @Mock
+ private SearchManager mSearchManager;
+ @Mock
+ private PackageManager mPackageManager;
private DefaultAssistPreferenceController mController;
@Before
@@ -61,4 +83,33 @@
assertThat(appInfo.getKey()).isEqualTo(flattenKey);
}
+
+ @Test
+ public void getSettingIntent_noSettingsActivity_shouldNotCrash() {
+ final String flattenKey = "com.android.settings/assist";
+ ShadowSecureSettings.putString(null, Settings.Secure.ASSISTANT, flattenKey);
+ when(mContext.getPackageManager()).thenReturn(mPackageManager);
+ DefaultAssistPreferenceController controller =
+ spy(new DefaultAssistPreferenceController(mContext));
+ final ResolveInfo resolveInfo = new ResolveInfo();
+ resolveInfo.activityInfo = new ActivityInfo();
+ resolveInfo.activityInfo.name = "assist";
+ resolveInfo.activityInfo.applicationInfo = new ApplicationInfo();
+ resolveInfo.activityInfo.applicationInfo.packageName = "com.android.settings";
+ when(mPackageManager.resolveActivityAsUser(any(Intent.class), anyInt(), anyInt()))
+ .thenReturn(resolveInfo);
+ when(mContext.getSystemService(Context.SEARCH_SERVICE)).thenReturn(mSearchManager);
+ when(mSearchManager.getAssistIntent(anyBoolean())).thenReturn(mock(Intent.class));
+ final ServiceInfo serviceInfo = new ServiceInfo();
+ serviceInfo.permission = Manifest.permission.BIND_VOICE_INTERACTION;
+ resolveInfo.serviceInfo = serviceInfo;
+ final List<ResolveInfo> services = new ArrayList<>();
+ services.add(resolveInfo);
+ when(mPackageManager.queryIntentServices(any(Intent.class), anyInt())).thenReturn(services);
+ doReturn(null).when(controller).getAssistSettingsActivity(
+ ComponentName.unflattenFromString(flattenKey), resolveInfo, mPackageManager);
+
+ controller.getSettingIntent(null);
+ // should not crash
+ }
}
diff --git a/tests/unit/src/com/android/settings/deviceinfo/storage/StorageAsyncLoaderTest.java b/tests/unit/src/com/android/settings/deviceinfo/storage/StorageAsyncLoaderTest.java
index e82482e..f7131b3 100644
--- a/tests/unit/src/com/android/settings/deviceinfo/storage/StorageAsyncLoaderTest.java
+++ b/tests/unit/src/com/android/settings/deviceinfo/storage/StorageAsyncLoaderTest.java
@@ -180,6 +180,20 @@
assertThat(result.get(PRIMARY_USER_ID).otherAppsSize).isEqualTo(0);
}
+ @Test
+ public void testRemovedPackageDoesNotCrash() throws Exception {
+ ApplicationInfo info = new ApplicationInfo();
+ info.packageName = PACKAGE_NAME_1;
+ info.category = ApplicationInfo.CATEGORY_UNDEFINED;
+ mInfo.add(info);
+ when(mSource.getStatsForPackage(anyString(), anyString(), any(UserHandle.class)))
+ .thenThrow(new IllegalStateException());
+
+ SparseArray<StorageAsyncLoader.AppsStorageResult> result = mLoader.loadInBackground();
+
+ // Should not crash.
+ }
+
private ApplicationInfo addPackage(
String packageName, long cacheSize, long codeSize, long dataSize, int category) {
StorageStatsSource.AppStorageStats storageStats =