summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/role/IRoleManager.aidl2
-rw-r--r--core/java/android/app/role/RoleManager.java18
-rw-r--r--core/java/android/content/pm/PackageUserState.java23
-rw-r--r--core/java/android/util/DebugUtils.java3
-rw-r--r--core/java/com/android/internal/util/CollectionUtils.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt3
-rw-r--r--services/core/java/com/android/server/IntentResolver.java30
-rw-r--r--services/core/java/com/android/server/TEST_MAPPING15
-rw-r--r--services/core/java/com/android/server/pm/ComponentResolver.java46
-rw-r--r--services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java97
-rw-r--r--services/core/java/com/android/server/role/RoleManagerService.java48
-rw-r--r--services/core/java/com/android/server/role/RoleUserState.java30
-rw-r--r--services/core/java/com/android/server/wm/WindowTraceBuffer.java181
-rw-r--r--services/core/java/com/android/server/wm/WindowTraceQueueBuffer.java88
-rw-r--r--services/core/java/com/android/server/wm/WindowTracing.java102
-rw-r--r--services/java/com/android/server/SystemServer.java4
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTraceBufferTest.java115
-rw-r--r--telephony/java/com/android/internal/telephony/SmsApplication.java117
-rw-r--r--tools/aapt2/Android.bp1
20 files changed, 788 insertions, 151 deletions
diff --git a/core/java/android/app/role/IRoleManager.aidl b/core/java/android/app/role/IRoleManager.aidl
index 0c9b41bf8d68..2964fbc0084b 100644
--- a/core/java/android/app/role/IRoleManager.aidl
+++ b/core/java/android/app/role/IRoleManager.aidl
@@ -50,4 +50,6 @@ interface IRoleManager {
boolean removeRoleHolderFromController(in String roleName, in String packageName);
List<String> getHeldRolesFromController(in String packageName);
+
+ String getDefaultSmsPackage(int userId);
}
diff --git a/core/java/android/app/role/RoleManager.java b/core/java/android/app/role/RoleManager.java
index d73e73f442d6..f14c56892807 100644
--- a/core/java/android/app/role/RoleManager.java
+++ b/core/java/android/app/role/RoleManager.java
@@ -19,6 +19,7 @@ package android.app.role;
import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
@@ -592,7 +593,6 @@ public final class RoleManager {
}
}
-
/**
* Returns the list of all roles that the given package is currently holding
*
@@ -613,6 +613,22 @@ public final class RoleManager {
}
}
+ /**
+ * Allows getting the role holder for {@link #ROLE_SMS} without
+ * {@link Manifest.permission#OBSERVE_ROLE_HOLDERS}, as required by
+ * {@link android.provider.Telephony.Sms#getDefaultSmsPackage(Context)}
+ *
+ * @hide
+ */
+ @Nullable
+ public String getDefaultSmsPackage(@UserIdInt int userId) {
+ try {
+ return mService.getDefaultSmsPackage(userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
private static class RoleManagerCallbackDelegate extends IRoleManagerCallback.Stub {
@NonNull
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
index be6ed51e3c89..74dd08fc1d6b 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -29,8 +29,11 @@ import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
import android.annotation.UnsupportedAppUsage;
import android.os.BaseBundle;
+import android.os.Debug;
import android.os.PersistableBundle;
import android.util.ArraySet;
+import android.util.DebugUtils;
+import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
@@ -43,6 +46,9 @@ import java.util.Objects;
* @hide
*/
public class PackageUserState {
+ private static final boolean DEBUG = false;
+ private static final String LOG_TAG = "PackageUserState";
+
public long ceDataInode;
public boolean installed;
public boolean stopped;
@@ -132,12 +138,12 @@ public class PackageUserState {
final boolean isSystemApp = componentInfo.applicationInfo.isSystemApp();
final boolean matchUninstalled = (flags & PackageManager.MATCH_KNOWN_PACKAGES) != 0;
if (!isAvailable(flags)
- && !(isSystemApp && matchUninstalled)) return false;
- if (!isEnabled(componentInfo, flags)) return false;
+ && !(isSystemApp && matchUninstalled)) return reportIfDebug(false, flags);
+ if (!isEnabled(componentInfo, flags)) return reportIfDebug(false, flags);
if ((flags & MATCH_SYSTEM_ONLY) != 0) {
if (!isSystemApp) {
- return false;
+ return reportIfDebug(false, flags);
}
}
@@ -145,7 +151,16 @@ public class PackageUserState {
&& !componentInfo.directBootAware;
final boolean matchesAware = ((flags & MATCH_DIRECT_BOOT_AWARE) != 0)
&& componentInfo.directBootAware;
- return matchesUnaware || matchesAware;
+ return reportIfDebug(matchesUnaware || matchesAware, flags);
+ }
+
+ private boolean reportIfDebug(boolean result, int flags) {
+ if (DEBUG && !result) {
+ Slog.i(LOG_TAG, "No match!; flags: "
+ + DebugUtils.flagsToString(PackageManager.class, "MATCH_", flags) + " "
+ + Debug.getCaller());
+ }
+ return result;
}
/**
diff --git a/core/java/android/util/DebugUtils.java b/core/java/android/util/DebugUtils.java
index af73a16e012a..265b0d303f7f 100644
--- a/core/java/android/util/DebugUtils.java
+++ b/core/java/android/util/DebugUtils.java
@@ -18,6 +18,7 @@ package android.util;
import android.annotation.UnsupportedAppUsage;
import android.os.Build;
+
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
@@ -250,7 +251,7 @@ public class DebugUtils {
if (value == 0 && flagsWasZero) {
return constNameWithoutPrefix(prefix, field);
}
- if ((flags & value) != 0) {
+ if ((flags & value) == value) {
flags &= ~value;
res.append(constNameWithoutPrefix(prefix, field)).append('|');
}
diff --git a/core/java/com/android/internal/util/CollectionUtils.java b/core/java/com/android/internal/util/CollectionUtils.java
index 470533f2d002..605df040da59 100644
--- a/core/java/com/android/internal/util/CollectionUtils.java
+++ b/core/java/com/android/internal/util/CollectionUtils.java
@@ -330,4 +330,18 @@ public class CollectionUtils {
public static @NonNull <T> List<T> defeatNullable(@Nullable List<T> val) {
return (val != null) ? val : Collections.emptyList();
}
+
+ /**
+ * @return the first element if not empty/null, null otherwise
+ */
+ public static @Nullable <T> T firstOrNull(@Nullable List<T> cur) {
+ return isEmpty(cur) ? null : cur.get(0);
+ }
+
+ /**
+ * @return list of single given element if it's not null, empty list otherwise
+ */
+ public static @NonNull <T> List<T> singletonOrEmpty(@Nullable T item) {
+ return item == null ? Collections.emptyList() : Collections.singletonList(item);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
index 4388200709fc..6ed1ebad8e51 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
@@ -119,12 +119,13 @@ class OngoingPrivacyDialog constructor(
appName.text = app.applicationName
if (showIcons) {
- dialogBuilder.generateIconsForApp(types).forEach {
+ dialogBuilder.generateIconsForApp(types).forEachIndexed { index, it ->
it.setBounds(0, 0, iconSize, iconSize)
val image = ImageView(context).apply {
imageTintList = ColorStateList.valueOf(iconColor)
setImageDrawable(it)
}
+ image.contentDescription = types[index].getName(context)
icons.addView(image, lp)
}
icons.visibility = View.VISIBLE
diff --git a/services/core/java/com/android/server/IntentResolver.java b/services/core/java/com/android/server/IntentResolver.java
index ea80ac1aecef..80fda19aa10c 100644
--- a/services/core/java/com/android/server/IntentResolver.java
+++ b/services/core/java/com/android/server/IntentResolver.java
@@ -16,32 +16,31 @@
package com.android.server;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Set;
-
+import android.content.Intent;
+import android.content.IntentFilter;
import android.net.Uri;
-import android.util.FastImmutableArraySet;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.FastImmutableArraySet;
import android.util.Log;
+import android.util.LogPrinter;
import android.util.MutableInt;
import android.util.PrintWriterPrinter;
-import android.util.Slog;
-import android.util.LogPrinter;
import android.util.Printer;
-
-import android.content.Intent;
-import android.content.IntentFilter;
+import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import com.android.internal.util.FastPrintWriter;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
/**
* {@hide}
*/
@@ -788,6 +787,7 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> {
+ filter.hasCategory(Intent.CATEGORY_DEFAULT));
if (!defaultOnly || filter.hasCategory(Intent.CATEGORY_DEFAULT)) {
final R oneResult = newResult(filter, match, userId);
+ if (debug) Slog.v(TAG, " Created result: " + oneResult);
if (oneResult != null) {
dest.add(oneResult);
if (debug) {
diff --git a/services/core/java/com/android/server/TEST_MAPPING b/services/core/java/com/android/server/TEST_MAPPING
new file mode 100644
index 000000000000..93e1dd36797a
--- /dev/null
+++ b/services/core/java/com/android/server/TEST_MAPPING
@@ -0,0 +1,15 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksMockingServicesTests",
+ "options": [
+ {
+ "include-annotation": "android.platform.test.annotations.Presubmit"
+ },
+ {
+ "exclude-annotation": "android.support.test.filters.FlakyTest"
+ }
+ ]
+ }
+ ]
+}
diff --git a/services/core/java/com/android/server/pm/ComponentResolver.java b/services/core/java/com/android/server/pm/ComponentResolver.java
index 3b11525e7cda..8facce112b52 100644
--- a/services/core/java/com/android/server/pm/ComponentResolver.java
+++ b/services/core/java/com/android/server/pm/ComponentResolver.java
@@ -42,6 +42,7 @@ import android.content.pm.ServiceInfo;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.DebugUtils;
import android.util.Log;
import android.util.LogPrinter;
import android.util.Pair;
@@ -60,6 +61,7 @@ import java.util.Set;
/** Resolves all Android component types [activities, services, providers and receivers]. */
public class ComponentResolver {
+ private static final boolean DEBUG = false;
private static final String TAG = "PackageManager";
private static final boolean DEBUG_FILTERS = false;
private static final boolean DEBUG_SHOW_INFO = false;
@@ -1198,22 +1200,48 @@ public class ComponentResolver {
return packageName.equals(info.activity.owner.packageName);
}
+ private void log(String reason, ActivityIntentInfo info, int match,
+ int userId) {
+ Slog.w(TAG, reason
+ + "; match: "
+ + DebugUtils.flagsToString(IntentFilter.class, "MATCH_", match)
+ + "; userId: " + userId
+ + "; intent info: " + info);
+ }
+
@Override
protected ResolveInfo newResult(PackageParser.ActivityIntentInfo info,
int match, int userId) {
- if (!sUserManager.exists(userId)) return null;
+ if (!sUserManager.exists(userId)) {
+ if (DEBUG) {
+ log("User doesn't exist", info, match, userId);
+ }
+ return null;
+ }
if (!sPackageManagerInternal.isEnabledAndMatches(info.activity.info, mFlags, userId)) {
+ if (DEBUG) {
+ log("!PackageManagerInternal.isEnabledAndMatches; mFlags="
+ + DebugUtils.flagsToString(PackageManager.class, "MATCH_", mFlags),
+ info, match, userId);
+ }
return null;
}
final PackageParser.Activity activity = info.activity;
PackageSetting ps = (PackageSetting) activity.owner.mExtras;
if (ps == null) {
+ if (DEBUG) {
+ log("info.activity.owner.mExtras == null", info, match, userId);
+ }
return null;
}
final PackageUserState userState = ps.readUserState(userId);
ActivityInfo ai =
PackageParser.generateActivityInfo(activity, mFlags, userState, userId);
if (ai == null) {
+ if (DEBUG) {
+ log("Failed to create ActivityInfo based on " + info.activity, info, match,
+ userId);
+ }
return null;
}
final boolean matchExplicitlyVisibleOnly =
@@ -1227,15 +1255,31 @@ public class ComponentResolver {
final boolean matchInstantApp = (mFlags & PackageManager.MATCH_INSTANT) != 0;
// throw out filters that aren't visible to ephemeral apps
if (matchVisibleToInstantApp && !(componentVisible || userState.instantApp)) {
+ if (DEBUG) {
+ log("Filter(s) not visible to ephemeral apps"
+ + "; matchVisibleToInstantApp=" + matchVisibleToInstantApp
+ + "; matchInstantApp=" + matchInstantApp
+ + "; info.isVisibleToInstantApp()=" + info.isVisibleToInstantApp()
+ + "; matchExplicitlyVisibleOnly=" + matchExplicitlyVisibleOnly
+ + "; info.isExplicitlyVisibleToInstantApp()="
+ + info.isExplicitlyVisibleToInstantApp(),
+ info, match, userId);
+ }
return null;
}
// throw out instant app filters if we're not explicitly requesting them
if (!matchInstantApp && userState.instantApp) {
+ if (DEBUG) {
+ log("Instant app filter is not explicitly requested", info, match, userId);
+ }
return null;
}
// throw out instant app filters if updates are available; will trigger
// instant app resolution
if (userState.instantApp && ps.isUpdateAvailable()) {
+ if (DEBUG) {
+ log("Instant app update is available", info, match, userId);
+ }
return null;
}
final ResolveInfo res = new ResolveInfo();
diff --git a/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java b/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java
new file mode 100644
index 000000000000..45c975b26956
--- /dev/null
+++ b/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2018 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.server.policy.role;
+
+import android.annotation.NonNull;
+import android.app.role.RoleManager;
+import android.content.Context;
+import android.os.Debug;
+import android.provider.Settings;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.telephony.SmsApplication;
+import com.android.internal.util.CollectionUtils;
+import com.android.server.role.RoleManagerService;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Logic to retrieve the various legacy(pre-Q) equivalents of role holders.
+ *
+ * Unlike {@link RoleManagerService} this is meant to be pretty high-level to allow for depending
+ * on all kinds of various systems that are historically involved in legacy role resolution,
+ * e.g. {@link SmsApplication}
+ *
+ * @see RoleManagerService#migrateRoleIfNecessary
+ */
+public class LegacyRoleResolutionPolicy implements RoleManagerService.RoleHoldersResolver {
+
+ private static final boolean DEBUG = false;
+ private static final String LOG_TAG = "LegacyRoleResolutionPol";
+
+ @NonNull
+ private final Context mContext;
+
+ public LegacyRoleResolutionPolicy(Context context) {
+ mContext = context;
+ }
+
+ @Override
+ public List<String> getRoleHolders(String roleName, int userId) {
+ switch (roleName) {
+ case RoleManager.ROLE_SMS: {
+ // Moved over from SmsApplication#getApplication
+ String result = Settings.Secure.getStringForUser(
+ mContext.getContentResolver(),
+ Settings.Secure.SMS_DEFAULT_APPLICATION, userId);
+
+ if (result == null) {
+ Collection<SmsApplication.SmsApplicationData> applications =
+ SmsApplication.getApplicationCollectionAsUser(mContext, userId);
+ SmsApplication.SmsApplicationData applicationData;
+ String defaultPackage = mContext.getResources()
+ .getString(com.android.internal.R.string.default_sms_application);
+ applicationData =
+ SmsApplication.getApplicationForPackage(applications, defaultPackage);
+
+ if (applicationData == null) {
+ // Are there any applications?
+ if (applications.size() != 0) {
+ applicationData =
+ (SmsApplication.SmsApplicationData) applications.toArray()[0];
+ }
+ }
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Found default sms app: " + applicationData
+ + " among: " + applications + " from " + Debug.getCallers(4));
+ }
+ SmsApplication.SmsApplicationData app = applicationData;
+ result = app == null ? null : app.mPackageName;
+ }
+
+ return CollectionUtils.singletonOrEmpty(result);
+ }
+ default: {
+ Slog.e(LOG_TAG, "Don't know how to find legacy role holders for " + roleName);
+ return Collections.emptyList();
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java
index f37ca12bbd7f..b4883371b468 100644
--- a/services/core/java/com/android/server/role/RoleManagerService.java
+++ b/services/core/java/com/android/server/role/RoleManagerService.java
@@ -35,6 +35,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManagerInternal;
import android.content.pm.Signature;
+import android.os.Binder;
import android.os.Handler;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
@@ -92,6 +93,15 @@ public class RoleManagerService extends SystemService implements RoleUserState.C
@NonNull
private final Object mLock = new Object();
+ @NonNull
+ private final RoleHoldersResolver mLegacyRoleResolver;
+
+ /** @see #getRoleHolders(String, int) */
+ public interface RoleHoldersResolver {
+ /** @return a list of packages that hold a given role for a given user */
+ List<String> getRoleHolders(String roleName, int userId);
+ }
+
/**
* Maps user id to its state.
*/
@@ -118,9 +128,12 @@ public class RoleManagerService extends SystemService implements RoleUserState.C
@NonNull
private final Handler mListenerHandler = FgThread.getHandler();
- public RoleManagerService(@NonNull Context context) {
+ public RoleManagerService(@NonNull Context context,
+ @NonNull RoleHoldersResolver legacyRoleResolver) {
super(context);
+ mLegacyRoleResolver = legacyRoleResolver;
+
mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
mAppOpsManager = context.getSystemService(AppOpsManager.class);
@@ -175,10 +188,17 @@ public class RoleManagerService extends SystemService implements RoleUserState.C
private void performInitialGrantsIfNecessary(@UserIdInt int userId) {
RoleUserState userState;
userState = getOrCreateUserState(userId);
+
String packagesHash = computeComponentStateHash(userId);
String oldPackagesHash = userState.getPackagesHash();
boolean needGrant = !Objects.equals(packagesHash, oldPackagesHash);
if (needGrant) {
+
+ //TODO gradually add more role migrations statements here for remaining roles
+ // Make sure to implement LegacyRoleResolutionPolicy#getRoleHolders
+ // for a given role before adding a migration statement for it here
+ migrateRoleIfNecessary(RoleManager.ROLE_SMS, userId);
+
// Some vital packages state has changed since last role grant
// Run grants again
Slog.i(LOG_TAG, "Granting default permissions...");
@@ -205,6 +225,20 @@ public class RoleManagerService extends SystemService implements RoleUserState.C
}
}
+ private void migrateRoleIfNecessary(String role, @UserIdInt int userId) {
+ // Any role for which we have a record are already migrated
+ RoleUserState userState = getOrCreateUserState(userId);
+ if (!userState.isRoleAvailable(role)) {
+ userState.addRoleName(role);
+ List<String> roleHolders = mLegacyRoleResolver.getRoleHolders(role, userId);
+ Slog.i(LOG_TAG, "Migrating " + role + ", legacy holders: " + roleHolders);
+ int size = roleHolders.size();
+ for (int i = 0; i < size; i++) {
+ userState.addRoleHolder(role, roleHolders.get(i));
+ }
+ }
+ }
+
@Nullable
private static String computeComponentStateHash(@UserIdInt int userId) {
PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
@@ -372,6 +406,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C
@Nullable
private ArraySet<String> getRoleHoldersInternal(@NonNull String roleName,
@UserIdInt int userId) {
+ migrateRoleIfNecessary(roleName, userId);
RoleUserState userState = getOrCreateUserState(userId);
return userState.getRoleHolders(roleName);
}
@@ -530,6 +565,17 @@ public class RoleManagerService extends SystemService implements RoleUserState.C
}
@Override
+ public String getDefaultSmsPackage(int userId) {
+ long identity = Binder.clearCallingIdentity();
+ try {
+ return CollectionUtils.firstOrNull(
+ getRoleHoldersAsUser(RoleManager.ROLE_SMS, userId));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter fout,
@Nullable String[] args) {
if (!DumpUtils.checkDumpPermission(getContext(), LOG_TAG, fout)) {
diff --git a/services/core/java/com/android/server/role/RoleUserState.java b/services/core/java/com/android/server/role/RoleUserState.java
index 630a39caeb08..69e144951154 100644
--- a/services/core/java/com/android/server/role/RoleUserState.java
+++ b/services/core/java/com/android/server/role/RoleUserState.java
@@ -205,6 +205,28 @@ public class RoleUserState {
}
/**
+ * Adds the given role, effectively marking it as {@link #isRoleAvailable available}
+ *
+ * @param roleName the name of the role
+ *
+ * @return whether any changes were made
+ */
+ public boolean addRoleName(@NonNull String roleName) {
+ synchronized (mLock) {
+ throwIfDestroyedLocked();
+
+ if (!mRoles.containsKey(roleName)) {
+ mRoles.put(roleName, new ArraySet<>());
+ Slog.i(LOG_TAG, "Added new role: " + roleName);
+ scheduleWriteFileLocked();
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+
+ /**
* Set the names of all available roles.
*
* @param roleNames the names of all the available roles
@@ -231,13 +253,7 @@ public class RoleUserState {
int roleNamesSize = roleNames.size();
for (int i = 0; i < roleNamesSize; i++) {
- String roleName = roleNames.get(i);
-
- if (!mRoles.containsKey(roleName)) {
- mRoles.put(roleName, new ArraySet<>());
- Slog.i(LOG_TAG, "Added new role: " + roleName);
- changed = true;
- }
+ changed |= addRoleName(roleNames.get(i));
}
if (changed) {
diff --git a/services/core/java/com/android/server/wm/WindowTraceBuffer.java b/services/core/java/com/android/server/wm/WindowTraceBuffer.java
new file mode 100644
index 000000000000..936ee85697b8
--- /dev/null
+++ b/services/core/java/com/android/server/wm/WindowTraceBuffer.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2019 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.server.wm;
+
+import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER;
+import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER_H;
+import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER_L;
+
+import android.os.Trace;
+import android.util.proto.ProtoOutputStream;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+/**
+ * Buffer used for window tracing.
+ */
+abstract class WindowTraceBuffer {
+ private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L;
+
+ final Object mBufferSizeLock = new Object();
+ final BlockingQueue<byte[]> mBuffer;
+ int mBufferSize;
+ private final int mBufferCapacity;
+ private final File mTraceFile;
+
+ WindowTraceBuffer(int size, File traceFile) throws IOException {
+ mBufferCapacity = size;
+ mTraceFile = traceFile;
+ mBuffer = new LinkedBlockingQueue<>();
+
+ initTraceFile();
+ }
+
+ int getAvailableSpace() {
+ return mBufferCapacity - mBufferSize;
+ }
+
+ /**
+ * Inserts the specified element into this buffer.
+ *
+ * This method is synchronized with {@code #take()} and {@code #clear()}
+ * for consistency.
+ *
+ * @param proto the element to add
+ * @return {@code true} if the inserted item was inserted into the buffer
+ * @throws IllegalStateException if the element cannot be added because it is larger
+ * than the buffer size.
+ */
+ boolean add(ProtoOutputStream proto) throws InterruptedException {
+ byte[] protoBytes = proto.getBytes();
+ int protoLength = protoBytes.length;
+ if (protoLength > mBufferCapacity) {
+ throw new IllegalStateException("Trace object too large for the buffer. Buffer size:"
+ + mBufferCapacity + " Object size: " + protoLength);
+ }
+ synchronized (mBufferSizeLock) {
+ boolean canAdd = canAdd(protoBytes);
+ if (canAdd) {
+ mBuffer.offer(protoBytes);
+ mBufferSize += protoLength;
+ }
+ return canAdd;
+ }
+ }
+
+ void writeNextBufferElementToFile() throws IOException {
+ byte[] proto;
+ try {
+ proto = take();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ return;
+ }
+
+ try {
+ Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "writeToFile");
+ try (OutputStream os = new FileOutputStream(mTraceFile, true)) {
+ os.write(proto);
+ }
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+ }
+ }
+
+ /**
+ * Retrieves and removes the head of this queue, waiting if necessary
+ * until an element becomes available.
+ *
+ * This method is synchronized with {@code #add(ProtoOutputStream)} and {@code #clear()}
+ * for consistency.
+ *
+ * @return the head of this buffer, or {@code null} if this buffer is empty
+ */
+ private byte[] take() throws InterruptedException {
+ byte[] item = mBuffer.take();
+ synchronized (mBufferSizeLock) {
+ mBufferSize -= item.length;
+ return item;
+ }
+ }
+
+ private void initTraceFile() throws IOException {
+ mTraceFile.delete();
+ try (OutputStream os = new FileOutputStream(mTraceFile)) {
+ mTraceFile.setReadable(true, false);
+ ProtoOutputStream proto = new ProtoOutputStream(os);
+ proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE);
+ proto.flush();
+ }
+ }
+
+ /**
+ * Checks if the element can be added to the buffer. The element is already certain to be
+ * smaller than the overall buffer size.
+ *
+ * @param protoBytes byte array representation of the Proto object to add
+ * @return <tt>true<</tt> if the element can be added to the buffer or not
+ */
+ abstract boolean canAdd(byte[] protoBytes) throws InterruptedException;
+
+ /**
+ * Flush all buffer content to the disk.
+ *
+ * @throws IOException if the buffer cannot write its contents to the {@link #mTraceFile}
+ */
+ abstract void writeToDisk() throws IOException, InterruptedException;
+
+ /**
+ * Builder for a {@code WindowTraceBuffer} which creates a {@link WindowTraceQueueBuffer}
+ */
+ static class Builder {
+ private File mTraceFile;
+ private int mBufferCapacity;
+
+
+ Builder setTraceFile(File traceFile) {
+ mTraceFile = traceFile;
+ return this;
+ }
+
+ Builder setBufferCapacity(int size) {
+ mBufferCapacity = size;
+ return this;
+ }
+
+ File getFile() {
+ return mTraceFile;
+ }
+
+ WindowTraceBuffer build() throws IOException {
+ if (mBufferCapacity <= 0) {
+ throw new IllegalStateException("Buffer capacity must be greater than 0.");
+ }
+
+ if (mTraceFile == null) {
+ throw new IllegalArgumentException("A valid trace file must be specified.");
+ }
+
+ return new WindowTraceQueueBuffer(mBufferCapacity, mTraceFile);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/WindowTraceQueueBuffer.java b/services/core/java/com/android/server/wm/WindowTraceQueueBuffer.java
new file mode 100644
index 000000000000..b7fc7ac8cb5e
--- /dev/null
+++ b/services/core/java/com/android/server/wm/WindowTraceQueueBuffer.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2019 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.server.wm;
+
+import static android.os.Build.IS_USER;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * A buffer structure backed by a {@link java.util.concurrent.BlockingQueue} to store the first
+ * {@code #size size} bytes of window trace elements.
+ * Once the buffer is full it will no longer accepts new elements.
+ */
+class WindowTraceQueueBuffer extends WindowTraceBuffer {
+ private Thread mWriterThread;
+ private boolean mCancel;
+
+ @VisibleForTesting
+ WindowTraceQueueBuffer(int size, File traceFile, boolean startWriterThread) throws IOException {
+ super(size, traceFile);
+ if (startWriterThread) {
+ initializeWriterThread();
+ }
+ }
+
+ WindowTraceQueueBuffer(int size, File traceFile) throws IOException {
+ this(size, traceFile, !IS_USER);
+ }
+
+ private void initializeWriterThread() {
+ mCancel = false;
+ mWriterThread = new Thread(() -> {
+ try {
+ loop();
+ } catch (IOException e) {
+ throw new IllegalStateException("Failed to execute trace write loop thread", e);
+ }
+ }, "window_tracing");
+ mWriterThread.start();
+ }
+
+ private void loop() throws IOException {
+ while (!mCancel) {
+ writeNextBufferElementToFile();
+ }
+ }
+
+ private void restartWriterThread() throws InterruptedException {
+ if (mWriterThread != null) {
+ mCancel = true;
+ mWriterThread.interrupt();
+ mWriterThread.join();
+ initializeWriterThread();
+ }
+ }
+
+ @Override
+ boolean canAdd(byte[] protoBytes) {
+ long availableSpace = getAvailableSpace();
+ return availableSpace >= protoBytes.length;
+ }
+
+ @Override
+ void writeToDisk() throws InterruptedException {
+ while (!mBuffer.isEmpty()) {
+ mBufferSizeLock.wait();
+ mBufferSizeLock.notify();
+ }
+ restartWriterThread();
+ }
+}
diff --git a/services/core/java/com/android/server/wm/WindowTracing.java b/services/core/java/com/android/server/wm/WindowTracing.java
index 8fa56bb065c6..63539c4f9fd9 100644
--- a/services/core/java/com/android/server/wm/WindowTracing.java
+++ b/services/core/java/com/android/server/wm/WindowTracing.java
@@ -17,31 +17,23 @@
package com.android.server.wm;
import static android.os.Build.IS_USER;
+
import static com.android.server.wm.WindowManagerTraceFileProto.ENTRY;
-import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER;
-import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER_H;
-import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER_L;
import static com.android.server.wm.WindowManagerTraceProto.ELAPSED_REALTIME_NANOS;
import static com.android.server.wm.WindowManagerTraceProto.WHERE;
import static com.android.server.wm.WindowManagerTraceProto.WINDOW_MANAGER_SERVICE;
+import android.annotation.Nullable;
import android.content.Context;
import android.os.ShellCommand;
import android.os.SystemClock;
import android.os.Trace;
-import android.annotation.Nullable;
import android.util.Log;
import android.util.proto.ProtoOutputStream;
-import com.android.internal.annotations.VisibleForTesting;
-
import java.io.File;
-import java.io.FileOutputStream;
import java.io.IOException;
-import java.io.OutputStream;
import java.io.PrintWriter;
-import java.util.concurrent.ArrayBlockingQueue;
-import java.util.concurrent.BlockingQueue;
/**
* A class that allows window manager to dump its state continuously to a trace file, such that a
@@ -49,35 +41,42 @@ import java.util.concurrent.BlockingQueue;
*/
class WindowTracing {
+ /**
+ * Maximum buffer size, currently defined as 512 KB
+ * Size was experimentally defined to fit between 100 to 150 elements.
+ */
+ private static final int WINDOW_TRACE_BUFFER_SIZE = 512 * 1024;
private static final String TAG = "WindowTracing";
- private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L;
private final Object mLock = new Object();
- private final File mTraceFile;
- private final BlockingQueue<ProtoOutputStream> mWriteQueue = new ArrayBlockingQueue<>(200);
+ private final WindowTraceBuffer.Builder mBufferBuilder;
+
+ private WindowTraceBuffer mTraceBuffer;
private boolean mEnabled;
private volatile boolean mEnabledLockFree;
WindowTracing(File file) {
- mTraceFile = file;
+ mBufferBuilder = new WindowTraceBuffer.Builder()
+ .setTraceFile(file)
+ .setBufferCapacity(WINDOW_TRACE_BUFFER_SIZE);
}
void startTrace(@Nullable PrintWriter pw) throws IOException {
- if (IS_USER){
+ if (IS_USER) {
logAndPrintln(pw, "Error: Tracing is not supported on user builds.");
return;
}
synchronized (mLock) {
- logAndPrintln(pw, "Start tracing to " + mTraceFile + ".");
- mWriteQueue.clear();
- mTraceFile.delete();
- try (OutputStream os = new FileOutputStream(mTraceFile)) {
- mTraceFile.setReadable(true, false);
- ProtoOutputStream proto = new ProtoOutputStream(os);
- proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE);
- proto.flush();
+ logAndPrintln(pw, "Start tracing to " + mBufferBuilder.getFile() + ".");
+ if (mTraceBuffer != null) {
+ try {
+ mTraceBuffer.writeToDisk();
+ } catch (InterruptedException e) {
+ logAndPrintln(pw, "Error: Unable to flush the previous buffer.");
+ }
}
+ mTraceBuffer = mBufferBuilder.build();
mEnabled = mEnabledLockFree = true;
}
}
@@ -91,67 +90,42 @@ class WindowTracing {
}
void stopTrace(@Nullable PrintWriter pw) {
- if (IS_USER){
+ if (IS_USER) {
logAndPrintln(pw, "Error: Tracing is not supported on user builds.");
return;
}
synchronized (mLock) {
- logAndPrintln(pw, "Stop tracing to " + mTraceFile + ". Waiting for traces to flush.");
+ logAndPrintln(pw, "Stop tracing to " + mBufferBuilder.getFile()
+ + ". Waiting for traces to flush.");
mEnabled = mEnabledLockFree = false;
- while (!mWriteQueue.isEmpty()) {
+
+ synchronized (mLock) {
if (mEnabled) {
logAndPrintln(pw, "ERROR: tracing was re-enabled while waiting for flush.");
throw new IllegalStateException("tracing enabled while waiting for flush.");
}
try {
- mLock.wait();
- mLock.notify();
+ mTraceBuffer.writeToDisk();
+ } catch (IOException e) {
+ Log.e(TAG, "Unable to write buffer to file", e);
} catch (InterruptedException e) {
- Thread.currentThread().interrupt();
+ Log.e(TAG, "Unable to interrupt window tracing file write thread", e);
}
}
- logAndPrintln(pw, "Trace written to " + mTraceFile + ".");
+ logAndPrintln(pw, "Trace written to " + mBufferBuilder.getFile() + ".");
}
}
- void appendTraceEntry(ProtoOutputStream proto) {
+ private void appendTraceEntry(ProtoOutputStream proto) {
if (!mEnabledLockFree) {
return;
}
- if (!mWriteQueue.offer(proto)) {
- Log.e(TAG, "Dropping window trace entry, queue full");
- }
- }
-
- void loop() {
- for (;;) {
- loopOnce();
- }
- }
-
- @VisibleForTesting
- void loopOnce() {
- ProtoOutputStream proto;
try {
- proto = mWriteQueue.take();
+ mTraceBuffer.add(proto);
} catch (InterruptedException e) {
+ Log.e(TAG, "Unable to add element to trace", e);
Thread.currentThread().interrupt();
- return;
- }
-
- synchronized (mLock) {
- try {
- Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "writeToFile");
- try (OutputStream os = new FileOutputStream(mTraceFile, true /* append */)) {
- os.write(proto.getBytes());
- }
- } catch (IOException e) {
- Log.e(TAG, "Failed to write file " + mTraceFile, e);
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
- }
- mLock.notify();
}
}
@@ -161,11 +135,7 @@ class WindowTracing {
static WindowTracing createDefaultAndStartLooper(Context context) {
File file = new File("/data/misc/wmtrace/wm_trace.pb");
- WindowTracing windowTracing = new WindowTracing(file);
- if (!IS_USER){
- new Thread(windowTracing::loop, "window_tracing").start();
- }
- return windowTracing;
+ return new WindowTracing(file);
}
int onShellCommand(ShellCommand shell, String cmd) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index e960fd164c6e..fef2db921dcf 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -120,6 +120,7 @@ import com.android.server.pm.PackageManagerService;
import com.android.server.pm.ShortcutService;
import com.android.server.pm.UserManagerService;
import com.android.server.policy.PhoneWindowManager;
+import com.android.server.policy.role.LegacyRoleResolutionPolicy;
import com.android.server.power.PowerManagerService;
import com.android.server.power.ShutdownThread;
import com.android.server.power.ThermalManagerService;
@@ -1991,7 +1992,8 @@ public final class SystemServer {
// Grants default permissions and defines roles
traceBeginAndSlog("StartRoleManagerService");
- mSystemServiceManager.startService(RoleManagerService.class);
+ mSystemServiceManager.startService(new RoleManagerService(
+ mSystemContext, new LegacyRoleResolutionPolicy(mSystemContext)));
traceEnd();
// No dependency on Webview preparation in system server. But this should
diff --git a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
index 148faada6381..6a153d5346ed 100644
--- a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
@@ -60,6 +60,7 @@ import android.os.Handler;
import android.os.Looper;
import android.os.PowerManager;
import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
import android.provider.Settings;
import android.util.Log;
import android.util.SparseArray;
@@ -81,6 +82,7 @@ import org.mockito.quality.Strictness;
import java.util.ArrayList;
+@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
public class AlarmManagerServiceTest {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTraceBufferTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowTraceBufferTest.java
new file mode 100644
index 000000000000..8d834974148c
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTraceBufferTest.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2019 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.server.wm;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER;
+import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER_H;
+import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER_L;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.platform.test.annotations.Presubmit;
+import android.util.proto.ProtoOutputStream;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+
+
+/**
+ * Test class for {@link WindowTraceBuffer} and {@link WindowTraceQueueBuffer}.
+ *
+ * Build/Install/Run:
+ * atest WmTests:WindowTraceBufferTest
+ */
+@SmallTest
+@Presubmit
+public class WindowTraceBufferTest {
+ private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L;
+
+ private File mFile;
+
+ @Before
+ public void setUp() throws Exception {
+ final Context testContext = getInstrumentation().getContext();
+ mFile = testContext.getFileStreamPath("tracing_test.dat");
+ mFile.delete();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mFile.delete();
+ }
+
+ @Test
+ public void testTraceQueueBuffer_addItem() throws Exception {
+ ProtoOutputStream toWrite1 = getDummy(1);
+ ProtoOutputStream toWrite2 = getDummy(2);
+ ProtoOutputStream toWrite3 = getDummy(3);
+ byte[] toWrite1Bytes = toWrite1.getBytes();
+ byte[] toWrite2Bytes = toWrite2.getBytes();
+ byte[] toWrite3Bytes = toWrite3.getBytes();
+
+ final int objectSize = toWrite1.getBytes().length;
+ final int bufferCapacity = objectSize * 2;
+
+ final WindowTraceBuffer buffer = buildQueueBuffer(bufferCapacity);
+
+ buffer.add(toWrite1);
+ assertTrue("First element should be in the list",
+ buffer.mBuffer.stream().anyMatch(p -> Arrays.equals(p, toWrite1Bytes)));
+
+ buffer.add(toWrite2);
+ assertTrue("First element should be in the list",
+ buffer.mBuffer.stream().anyMatch(p -> Arrays.equals(p, toWrite1Bytes)));
+ assertTrue("Second element should be in the list",
+ buffer.mBuffer.stream().anyMatch(p -> Arrays.equals(p, toWrite2Bytes)));
+
+ buffer.add(toWrite3);
+
+ assertTrue("Third element should not be in the list",
+ buffer.mBuffer.stream().noneMatch(p -> Arrays.equals(p, toWrite3Bytes)));
+
+ assertEquals("Buffer should have 2 elements", buffer.mBuffer.size(), 2);
+ assertEquals(String.format("Buffer is full, used space should be %d", bufferCapacity),
+ buffer.mBufferSize, bufferCapacity);
+ assertEquals("Buffer is full, available space should be 0",
+ buffer.getAvailableSpace(), 0);
+ }
+
+ private ProtoOutputStream getDummy(int value) {
+ ProtoOutputStream toWrite = new ProtoOutputStream();
+ toWrite.write(MAGIC_NUMBER, value);
+ toWrite.flush();
+
+ return toWrite;
+ }
+
+ private WindowTraceBuffer buildQueueBuffer(int size) throws IOException {
+ return new WindowTraceQueueBuffer(size, mFile, false);
+ }
+}
diff --git a/telephony/java/com/android/internal/telephony/SmsApplication.java b/telephony/java/com/android/internal/telephony/SmsApplication.java
index 9874f809c0b4..a508b068065f 100644
--- a/telephony/java/com/android/internal/telephony/SmsApplication.java
+++ b/telephony/java/com/android/internal/telephony/SmsApplication.java
@@ -18,6 +18,8 @@ package com.android.internal.telephony;
import android.Manifest.permission;
import android.app.AppOpsManager;
+import android.app.role.RoleManager;
+import android.app.role.RoleManagerCallback;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -29,13 +31,12 @@ import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
-import android.content.res.Resources;
import android.net.Uri;
+import android.os.AsyncTask;
import android.os.Binder;
import android.os.Debug;
import android.os.Process;
import android.os.UserHandle;
-import android.provider.Settings;
import android.provider.Telephony;
import android.provider.Telephony.Sms.Intents;
import android.telephony.Rlog;
@@ -50,6 +51,10 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
/**
* Class for managing the primary application that we will deliver SMS/MMS messages to
@@ -67,6 +72,7 @@ public final class SmsApplication {
private static final String SCHEME_SMSTO = "smsto";
private static final String SCHEME_MMS = "mms";
private static final String SCHEME_MMSTO = "mmsto";
+ private static final boolean DEBUG = false;
private static final boolean DEBUG_MULTIUSER = false;
private static final int[] DEFAULT_APP_EXCLUSIVE_APPOPS = {
@@ -240,7 +246,11 @@ public final class SmsApplication {
// Get the list of apps registered for SMS
Intent intent = new Intent(Intents.SMS_DELIVER_ACTION);
- List<ResolveInfo> smsReceivers = packageManager.queryBroadcastReceiversAsUser(intent, 0,
+ if (DEBUG) {
+ intent.addFlags(Intent.FLAG_DEBUG_LOG_RESOLUTION);
+ }
+ List<ResolveInfo> smsReceivers = packageManager.queryBroadcastReceiversAsUser(intent,
+ PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
userId);
HashMap<String, SmsApplicationData> receivers = new HashMap<String, SmsApplicationData>();
@@ -266,7 +276,8 @@ public final class SmsApplication {
// Update any existing entries with mms receiver class
intent = new Intent(Intents.WAP_PUSH_DELIVER_ACTION);
intent.setDataAndType(null, "application/vnd.wap.mms-message");
- List<ResolveInfo> mmsReceivers = packageManager.queryBroadcastReceiversAsUser(intent, 0,
+ List<ResolveInfo> mmsReceivers = packageManager.queryBroadcastReceiversAsUser(intent,
+ PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
userId);
for (ResolveInfo resolveInfo : mmsReceivers) {
final ActivityInfo activityInfo = resolveInfo.activityInfo;
@@ -286,7 +297,8 @@ public final class SmsApplication {
// Update any existing entries with respond via message intent class.
intent = new Intent(TelephonyManager.ACTION_RESPOND_VIA_MESSAGE,
Uri.fromParts(SCHEME_SMSTO, "", null));
- List<ResolveInfo> respondServices = packageManager.queryIntentServicesAsUser(intent, 0,
+ List<ResolveInfo> respondServices = packageManager.queryIntentServicesAsUser(intent,
+ PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
userId);
for (ResolveInfo resolveInfo : respondServices) {
final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
@@ -306,7 +318,8 @@ public final class SmsApplication {
// Update any existing entries with supports send to.
intent = new Intent(Intent.ACTION_SENDTO,
Uri.fromParts(SCHEME_SMSTO, "", null));
- List<ResolveInfo> sendToActivities = packageManager.queryIntentActivitiesAsUser(intent, 0,
+ List<ResolveInfo> sendToActivities = packageManager.queryIntentActivitiesAsUser(intent,
+ PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
userId);
for (ResolveInfo resolveInfo : sendToActivities) {
final ActivityInfo activityInfo = resolveInfo.activityInfo;
@@ -323,7 +336,9 @@ public final class SmsApplication {
// Update any existing entries with the default sms changed handler.
intent = new Intent(Telephony.Sms.Intents.ACTION_DEFAULT_SMS_PACKAGE_CHANGED);
List<ResolveInfo> smsAppChangedReceivers =
- packageManager.queryBroadcastReceiversAsUser(intent, 0, userId);
+ packageManager.queryBroadcastReceiversAsUser(intent,
+ PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
if (DEBUG_MULTIUSER) {
Log.i(LOG_TAG, "getApplicationCollectionInternal smsAppChangedActivities=" +
smsAppChangedReceivers);
@@ -348,7 +363,9 @@ public final class SmsApplication {
// Update any existing entries with the external provider changed handler.
intent = new Intent(Telephony.Sms.Intents.ACTION_EXTERNAL_PROVIDER_CHANGE);
List<ResolveInfo> providerChangedReceivers =
- packageManager.queryBroadcastReceiversAsUser(intent, 0, userId);
+ packageManager.queryBroadcastReceiversAsUser(intent,
+ PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
if (DEBUG_MULTIUSER) {
Log.i(LOG_TAG, "getApplicationCollectionInternal providerChangedActivities=" +
providerChangedReceivers);
@@ -373,7 +390,9 @@ public final class SmsApplication {
// Update any existing entries with the sim full handler.
intent = new Intent(Intents.SIM_FULL_ACTION);
List<ResolveInfo> simFullReceivers =
- packageManager.queryBroadcastReceiversAsUser(intent, 0, userId);
+ packageManager.queryBroadcastReceiversAsUser(intent,
+ PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
if (DEBUG_MULTIUSER) {
Log.i(LOG_TAG, "getApplicationCollectionInternal simFullReceivers="
+ simFullReceivers);
@@ -406,7 +425,8 @@ public final class SmsApplication {
if (smsApplicationData != null) {
if (!smsApplicationData.isComplete()) {
Log.w(LOG_TAG, "Package " + packageName
- + " lacks required manifest declarations to be a default sms app");
+ + " lacks required manifest declarations to be a default sms app: "
+ + smsApplicationData);
receivers.remove(packageName);
}
}
@@ -418,7 +438,7 @@ public final class SmsApplication {
* Checks to see if we have a valid installed SMS application for the specified package name
* @return Data for the specified package name or null if there isn't one
*/
- private static SmsApplicationData getApplicationForPackage(
+ public static SmsApplicationData getApplicationForPackage(
Collection<SmsApplicationData> applications, String packageName) {
if (packageName == null) {
return null;
@@ -456,8 +476,7 @@ public final class SmsApplication {
Log.i(LOG_TAG, "getApplication userId=" + userId);
}
// Determine which application receives the broadcast
- String defaultApplication = Settings.Secure.getStringForUser(context.getContentResolver(),
- Settings.Secure.SMS_DEFAULT_APPLICATION, userId);
+ String defaultApplication = getDefaultSmsPackage(context, userId);
if (DEBUG_MULTIUSER) {
Log.i(LOG_TAG, "getApplication defaultApp=" + defaultApplication);
}
@@ -469,27 +488,6 @@ public final class SmsApplication {
if (DEBUG_MULTIUSER) {
Log.i(LOG_TAG, "getApplication appData=" + applicationData);
}
- // Picking a new SMS app requires AppOps and Settings.Secure permissions, so we only do
- // this if the caller asked us to.
- if (updateIfNeeded && applicationData == null) {
- // Try to find the default SMS package for this device
- Resources r = context.getResources();
- String defaultPackage =
- r.getString(com.android.internal.R.string.default_sms_application);
- applicationData = getApplicationForPackage(applications, defaultPackage);
-
- if (applicationData == null) {
- // Are there any applications?
- if (applications.size() != 0) {
- applicationData = (SmsApplicationData)applications.toArray()[0];
- }
- }
-
- // If we found a new default app, update the setting
- if (applicationData != null) {
- setDefaultApplicationInternal(applicationData.mPackageName, context, userId);
- }
- }
// If we found a package, make sure AppOps permissions are set up correctly
if (applicationData != null) {
@@ -513,7 +511,7 @@ public final class SmsApplication {
// current SMS app will already be the preferred activity - but checking whether or
// not this is true is just as expensive as reconfiguring the preferred activity so
// we just reconfigure every time.
- updateDefaultSmsApp(context, userId, applicationData);
+ defaultSmsAppChanged(context);
}
}
if (DEBUG_MULTIUSER) {
@@ -522,16 +520,17 @@ public final class SmsApplication {
return applicationData;
}
- private static void updateDefaultSmsApp(Context context, int userId,
- SmsApplicationData applicationData) {
+ private static String getDefaultSmsPackage(Context context, int userId) {
+ return context.getSystemService(RoleManager.class).getDefaultSmsPackage(userId);
+ }
+
+ /**
+ * Grants various permissions and appops on sms app change
+ */
+ private static void defaultSmsAppChanged(Context context) {
PackageManager packageManager = context.getPackageManager();
AppOpsManager appOps = context.getSystemService(AppOpsManager.class);
- // Configure this as the preferred activity for SENDTO sms/mms intents
- configurePreferredActivity(packageManager, new ComponentName(
- applicationData.mPackageName, applicationData.mSendToClass),
- userId);
-
// Assign permission to special system apps
assignExclusiveSmsPermissionsToSystemApp(context, packageManager, appOps,
PHONE_PACKAGE_NAME);
@@ -603,8 +602,7 @@ public final class SmsApplication {
final UserHandle userHandle = UserHandle.of(userId);
// Get old package name
- String oldPackageName = Settings.Secure.getStringForUser(context.getContentResolver(),
- Settings.Secure.SMS_DEFAULT_APPLICATION, userId);
+ String oldPackageName = getDefaultSmsPackage(context, userId);
if (DEBUG_MULTIUSER) {
Log.i(LOG_TAG, "setDefaultApplicationInternal old=" + oldPackageName +
@@ -636,16 +634,29 @@ public final class SmsApplication {
}
}
- // Update the secure setting.
- Settings.Secure.putStringForUser(context.getContentResolver(),
- Settings.Secure.SMS_DEFAULT_APPLICATION, applicationData.mPackageName,
- userId);
-
- // Allow relevant appops for the newly configured default SMS app.
- setExclusiveAppops(applicationData.mPackageName, appOps, applicationData.mUid,
- AppOpsManager.MODE_ALLOWED);
+ // Update the setting.
+ CompletableFuture<Void> res = new CompletableFuture<>();
+ context.getSystemService(RoleManager.class).addRoleHolderAsUser(
+ RoleManager.ROLE_SMS, applicationData.mPackageName, UserHandle.of(userId),
+ AsyncTask.THREAD_POOL_EXECUTOR, new RoleManagerCallback() {
+ @Override
+ public void onSuccess() {
+ res.complete(null);
+ }
+
+ @Override
+ public void onFailure() {
+ res.completeExceptionally(new RuntimeException());
+ }
+ });
+ try {
+ res.get(5, TimeUnit.SECONDS);
+ } catch (InterruptedException | ExecutionException | TimeoutException e) {
+ Log.e(LOG_TAG, "Exception while adding sms role holder " + applicationData, e);
+ return;
+ }
- updateDefaultSmsApp(context, userId, applicationData);
+ defaultSmsAppChanged(context);
if (DEBUG_MULTIUSER) {
Log.i(LOG_TAG, "setDefaultApplicationInternal oldAppData=" + oldAppData);
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index c42a8889e373..0580df60f32a 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -58,6 +58,7 @@ cc_defaults {
"libprotobuf-cpp-lite",
"libz",
],
+ stl: "libc++_static",
group_static_libs: true,
}