diff options
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, } |