diff options
95 files changed, 4520 insertions, 661 deletions
diff --git a/api/system-current.txt b/api/system-current.txt index 8ee6e639cbe8..2843916c25b7 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -9309,7 +9309,7 @@ package android.telephony.ims.feature { field public static final int STATE_UNAVAILABLE = 0; // 0x0 } - public static class ImsFeature.Capabilities { + @Deprecated public static class ImsFeature.Capabilities { field @Deprecated protected int mCapabilities; } @@ -9343,7 +9343,7 @@ package android.telephony.ims.feature { public static class MmTelFeature.MmTelCapabilities extends android.telephony.ims.feature.ImsFeature.Capabilities { ctor public MmTelFeature.MmTelCapabilities(); ctor @Deprecated public MmTelFeature.MmTelCapabilities(android.telephony.ims.feature.ImsFeature.Capabilities); - ctor public MmTelFeature.MmTelCapabilities(int); + ctor public MmTelFeature.MmTelCapabilities(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int); method public final void addCapabilities(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int); method public final boolean isCapable(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int); method public final void removeCapabilities(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int); diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp index 14f48711e068..c9a4b3ba6368 100644 --- a/cmds/statsd/Android.bp +++ b/cmds/statsd/Android.bp @@ -204,6 +204,10 @@ cc_test { ], srcs: [ + // atom_field_options.proto needs field_options.proto, but that is + // not included in libprotobuf-cpp-lite, so compile it here. + ":libprotobuf-internal-protos", + "src/atom_field_options.proto", "src/atoms.proto", "src/stats_log.proto", @@ -262,11 +266,11 @@ cc_test { ], proto: { - type: "full", + type: "lite", include_dirs: ["external/protobuf/src"], }, - shared_libs: ["libprotobuf-cpp-full"], + shared_libs: ["libprotobuf-cpp-lite"], } @@ -279,6 +283,10 @@ cc_benchmark { defaults: ["statsd_defaults"], srcs: [ + // atom_field_options.proto needs field_options.proto, but that is + // not included in libprotobuf-cpp-lite, so compile it here. + ":libprotobuf-internal-protos", + "src/atom_field_options.proto", "src/atoms.proto", "src/stats_log.proto", @@ -293,7 +301,7 @@ cc_benchmark { ], proto: { - type: "full", + type: "lite", include_dirs: ["external/protobuf/src"], }, @@ -315,7 +323,7 @@ cc_benchmark { shared_libs: [ "libgtest_prod", "libstatslog", - "libprotobuf-cpp-full", + "libprotobuf-cpp-lite", ], } diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp index d1dcb5df7838..7ace44eef564 100644 --- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp +++ b/cmds/statsd/src/anomaly/AnomalyTracker.cpp @@ -24,6 +24,7 @@ #include "subscriber/IncidentdReporter.h" #include "subscriber/SubscriberReporter.h" +#include <inttypes.h> #include <statslog.h> #include <time.h> @@ -224,7 +225,7 @@ void AnomalyTracker::declareAnomaly(const int64_t& timestampNs, int64_t metricId } if (!mSubscriptions.empty()) { - ALOGI("An anomaly (%lld) %s has occurred! Informing subscribers.", + ALOGI("An anomaly (%" PRId64 ") %s has occurred! Informing subscribers.", mAlert.id(), key.toString().c_str()); informSubscribers(key, metricId, metricValue); } else { diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp index 111b4346151a..40484f4fb86b 100644 --- a/cmds/statsd/src/metrics/metrics_manager_util.cpp +++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp @@ -35,6 +35,8 @@ #include "stats_util.h" #include "statslog.h" +#include <inttypes.h> + using std::set; using std::string; using std::unordered_map; @@ -593,7 +595,7 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t for (int i = 0; i < config.no_report_metric_size(); ++i) { const auto no_report_metric = config.no_report_metric(i); if (metricMap.find(no_report_metric) == metricMap.end()) { - ALOGW("no_report_metric %lld not exist", no_report_metric); + ALOGW("no_report_metric %" PRId64 " not exist", no_report_metric); return false; } noReportMetricIds.insert(no_report_metric); diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index 1fd7e52314e3..92aabb591e03 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -18,6 +18,7 @@ package android.app; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.content.ComponentName; import android.content.IIntentReceiver; import android.content.IIntentSender; @@ -30,7 +31,6 @@ import android.net.Uri; import android.os.Bundle; import android.os.IBinder; import android.os.TransactionTooLargeException; -import android.view.RemoteAnimationAdapter; import java.util.ArrayList; import java.util.List; @@ -51,12 +51,12 @@ public abstract class ActivityManagerInternal { /** * Verify that calling app has access to the given provider. */ - public abstract String checkContentProviderAccess(String authority, int userId); + public abstract String checkContentProviderAccess(String authority, @UserIdInt int userId); /** * Verify that calling UID has access to the given provider. */ - public abstract int checkContentProviderUriPermission(Uri uri, int userId, + public abstract int checkContentProviderUriPermission(Uri uri, @UserIdInt int userId, int callingUid, int modeFlags); // Called by the power manager. @@ -71,7 +71,7 @@ public abstract class ActivityManagerInternal { /** * Kill foreground apps from the specified user. */ - public abstract void killForegroundAppsForUser(int userHandle); + public abstract void killForegroundAppsForUser(@UserIdInt int userId); /** * Sets how long a {@link PendingIntent} can be temporarily whitelist to by bypass restrictions @@ -174,7 +174,7 @@ public abstract class ActivityManagerInternal { * Checks to see if the calling pid is allowed to handle the user. Returns adjusted user id as * needed. */ - public abstract int handleIncomingUser(int callingPid, int callingUid, int userId, + public abstract int handleIncomingUser(int callingPid, int callingUid, @UserIdInt int userId, boolean allowAll, int allowMode, String name, String callerPackage); /** Checks if the calling binder pid as the permission. */ @@ -184,7 +184,7 @@ public abstract class ActivityManagerInternal { public abstract int getCurrentUserId(); /** Returns true if the user is running. */ - public abstract boolean isUserRunning(int userId, int flags); + public abstract boolean isUserRunning(@UserIdInt int userId, int flags); /** Trims memory usage in the system by removing/stopping unused application processes. */ public abstract void trimApplications(); @@ -211,7 +211,7 @@ public abstract class ActivityManagerInternal { * @param started */ public abstract void updateBatteryStats( - ComponentName activity, int uid, int userId, boolean resumed); + ComponentName activity, int uid, @UserIdInt int userId, boolean resumed); /** * Update UsageStats of the activity. @@ -222,23 +222,23 @@ public abstract class ActivityManagerInternal { * @param taskRoot TaskRecord's root */ public abstract void updateActivityUsageStats( - ComponentName activity, int userId, int event, IBinder appToken, + ComponentName activity, @UserIdInt int userId, int event, IBinder appToken, ComponentName taskRoot); public abstract void updateForegroundTimeIfOnBattery( String packageName, int uid, long cpuTimeDiff); - public abstract void sendForegroundProfileChanged(int userId); + public abstract void sendForegroundProfileChanged(@UserIdInt int userId); /** * Returns whether the given user requires credential entry at this time. This is used to * intercept activity launches for work apps when the Work Challenge is present. */ - public abstract boolean shouldConfirmCredentials(int userId); + public abstract boolean shouldConfirmCredentials(@UserIdInt int userId); public abstract int[] getCurrentProfileIds(); public abstract UserInfo getCurrentUser(); - public abstract void ensureNotSpecialUser(int userId); - public abstract boolean isCurrentProfile(int userId); - public abstract boolean hasStartedUserState(int userId); + public abstract void ensureNotSpecialUser(@UserIdInt int userId); + public abstract boolean isCurrentProfile(@UserIdInt int userId); + public abstract boolean hasStartedUserState(@UserIdInt int userId); public abstract void finishUserSwitch(Object uss); /** Schedule the execution of all pending app GCs. */ @@ -261,15 +261,16 @@ public abstract class ActivityManagerInternal { public abstract int broadcastIntentInPackage(String packageName, int uid, int realCallingUid, int realCallingPid, Intent intent, String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras, String requiredPermission, - Bundle bOptions, boolean serialized, boolean sticky, int userId, + Bundle bOptions, boolean serialized, boolean sticky, @UserIdInt int userId, boolean allowBackgroundActivityStarts); public abstract ComponentName startServiceInPackage(int uid, Intent service, - String resolvedType, boolean fgRequired, String callingPackage, int userId, + String resolvedType, boolean fgRequired, String callingPackage, @UserIdInt int userId, boolean allowBackgroundActivityStarts) throws TransactionTooLargeException; public abstract void disconnectActivityFromServices(Object connectionHolder, Object conns); - public abstract void cleanUpServices(int userId, ComponentName component, Intent baseIntent); - public abstract ActivityInfo getActivityInfoForUser(ActivityInfo aInfo, int userId); + public abstract void cleanUpServices(@UserIdInt int userId, ComponentName component, + Intent baseIntent); + public abstract ActivityInfo getActivityInfoForUser(ActivityInfo aInfo, @UserIdInt int userId); public abstract void ensureBootCompleted(); public abstract void updateOomLevelsForDisplay(int displayId); public abstract boolean isActivityStartsLoggingEnabled(); @@ -328,7 +329,7 @@ public abstract class ActivityManagerInternal { public abstract boolean isAppBad(ApplicationInfo info); /** Remove pending backup for the given userId. */ - public abstract void clearPendingBackup(int userId); + public abstract void clearPendingBackup(@UserIdInt int userId); /** * When power button is very long pressed, call this interface to do some pre-shutdown work diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 2a17800dd446..3418b7be42d6 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -5413,8 +5413,8 @@ public class Intent implements Parcelable, Cloneable { "android.intent.extra.ALLOW_MULTIPLE"; /** - * The integer userHandle carried with broadcast intents related to addition, removal and - * switching of users and managed profiles - {@link #ACTION_USER_ADDED}, + * The integer userHandle (i.e. userId) carried with broadcast intents related to addition, + * removal and switching of users and managed profiles - {@link #ACTION_USER_ADDED}, * {@link #ACTION_USER_REMOVED} and {@link #ACTION_USER_SWITCHED}. * * @hide diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java index 861ae7ba122e..9cf54f41a64b 100644 --- a/core/java/android/content/res/Configuration.java +++ b/core/java/android/content/res/Configuration.java @@ -1222,7 +1222,15 @@ public final class Configuration implements Parcelable, Comparable<Configuration .setVariant(variant) .setScript(script) .build(); - list.add(locale); + // Log a WTF here if a repeated locale is found to avoid throwing an + // exception in system server when LocaleList is created below + final int inListIndex = list.indexOf(locale); + if (inListIndex != -1) { + Slog.wtf(TAG, "Repeated locale (" + list.get(inListIndex) + ")" + + " found when trying to add: " + locale.toString()); + } else { + list.add(locale); + } } catch (IllformedLocaleException e) { Slog.e(TAG, "readFromProto error building locale with: " + "language-" + language + ";country-" + country diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java index 4e17f7e92013..0754dc78a629 100644 --- a/core/java/android/os/UserHandle.java +++ b/core/java/android/os/UserHandle.java @@ -128,8 +128,9 @@ public final class UserHandle implements Parcelable { @UnsupportedAppUsage public static final int AID_CACHE_GID_START = android.os.Process.FIRST_APPLICATION_CACHE_GID; + /** The userId represented by this UserHandle. */ @UnsupportedAppUsage - final int mHandle; + final @UserIdInt int mHandle; /** * Checks to see if the user id is the same for the two uids, i.e., they belong to the same @@ -270,7 +271,7 @@ public final class UserHandle implements Parcelable { } /** @hide */ - public static int getSharedAppGid(int userId, int appId) { + public static int getSharedAppGid(@UserIdInt int userId, @AppIdInt int appId) { if (appId >= AID_APP_START && appId <= AID_APP_END) { return (appId - AID_APP_START) + AID_SHARED_GID_START; } else if (appId >= AID_ROOT && appId <= AID_APP_START) { @@ -300,7 +301,7 @@ public final class UserHandle implements Parcelable { } /** @hide */ - public static int getCacheAppGid(int userId, int appId) { + public static int getCacheAppGid(@UserIdInt int userId, @AppIdInt int appId) { if (appId >= AID_APP_START && appId <= AID_APP_END) { return getUid(userId, (appId - AID_APP_START) + AID_CACHE_GID_START); } else { @@ -432,8 +433,8 @@ public final class UserHandle implements Parcelable { /** @hide */ @UnsupportedAppUsage - public UserHandle(int h) { - mHandle = h; + public UserHandle(@UserIdInt int userId) { + mHandle = userId; } /** diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index a7fa96bc1d90..baf748f4a20d 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -1794,14 +1794,14 @@ public class UserManager { /** * Returns the UserInfo object describing a specific user. * Requires {@link android.Manifest.permission#MANAGE_USERS} permission. - * @param userHandle the user handle of the user whose information is being requested. + * @param userId the user handle of the user whose information is being requested. * @return the UserInfo object for a specific user. * @hide */ @UnsupportedAppUsage - public UserInfo getUserInfo(@UserIdInt int userHandle) { + public UserInfo getUserInfo(@UserIdInt int userId) { try { - return mService.getUserInfo(userHandle); + return mService.getUserInfo(userId); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } @@ -2066,15 +2066,15 @@ public class UserManager { * * @param name the user's name * @param flags flags that identify the type of user and other properties. - * @param userHandle new user will be a profile of this user. + * @param userId new user will be a profile of this user. * * @return the {@link UserInfo} object for the created user, or null if the user * could not be created. * @hide */ @UnsupportedAppUsage - public UserInfo createProfileForUser(String name, int flags, @UserIdInt int userHandle) { - return createProfileForUser(name, flags, userHandle, null); + public UserInfo createProfileForUser(String name, int flags, @UserIdInt int userId) { + return createProfileForUser(name, flags, userId, null); } /** @@ -2084,17 +2084,17 @@ public class UserManager { * * @param name the user's name * @param flags flags that identify the type of user and other properties. - * @param userHandle new user will be a profile of this user. + * @param userId new user will be a profile of this user. * @param disallowedPackages packages that will not be installed in the profile being created. * * @return the {@link UserInfo} object for the created user, or null if the user * could not be created. * @hide */ - public UserInfo createProfileForUser(String name, int flags, @UserIdInt int userHandle, + public UserInfo createProfileForUser(String name, int flags, @UserIdInt int userId, String[] disallowedPackages) { try { - return mService.createProfileForUser(name, flags, userHandle, disallowedPackages); + return mService.createProfileForUser(name, flags, userId, disallowedPackages); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } @@ -2109,9 +2109,9 @@ public class UserManager { * @hide */ public UserInfo createProfileForUserEvenWhenDisallowed(String name, int flags, - @UserIdInt int userHandle, String[] disallowedPackages) { + @UserIdInt int userId, String[] disallowedPackages) { try { - return mService.createProfileForUserEvenWhenDisallowed(name, flags, userHandle, + return mService.createProfileForUserEvenWhenDisallowed(name, flags, userId, disallowedPackages); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); @@ -2280,12 +2280,12 @@ public class UserManager { * @hide * Marks the guest user for deletion to allow a new guest to be created before deleting * the current user who is a guest. - * @param userHandle + * @param userId * @return */ - public boolean markGuestForDeletion(@UserIdInt int userHandle) { + public boolean markGuestForDeletion(@UserIdInt int userId) { try { - return mService.markGuestForDeletion(userHandle); + return mService.markGuestForDeletion(userId); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } @@ -2317,16 +2317,16 @@ public class UserManager { * <p>Requires {@link android.Manifest.permission#MANAGE_USERS} and * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permissions. * - * @param userHandle the id of the user to become admin + * @param userId the id of the user to become admin * @hide */ @RequiresPermission(allOf = { Manifest.permission.INTERACT_ACROSS_USERS_FULL, Manifest.permission.MANAGE_USERS }) - public void setUserAdmin(@UserIdInt int userHandle) { + public void setUserAdmin(@UserIdInt int userId) { try { - mService.setUserAdmin(userHandle); + mService.setUserAdmin(userId); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } @@ -2337,9 +2337,9 @@ public class UserManager { * * @hide */ - public void evictCredentialEncryptionKey(@UserIdInt int userHandle) { + public void evictCredentialEncryptionKey(@UserIdInt int userId) { try { - mService.evictCredentialEncryptionKey(userHandle); + mService.evictCredentialEncryptionKey(userId); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } @@ -2400,9 +2400,9 @@ public class UserManager { Manifest.permission.INTERACT_ACROSS_USERS_FULL, Manifest.permission.MANAGE_USERS }) - public @Nullable String getUserAccount(@UserIdInt int userHandle) { + public @Nullable String getUserAccount(@UserIdInt int userId) { try { - return mService.getUserAccount(userHandle); + return mService.getUserAccount(userId); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } @@ -2416,9 +2416,9 @@ public class UserManager { Manifest.permission.INTERACT_ACROSS_USERS_FULL, Manifest.permission.MANAGE_USERS }) - public void setUserAccount(@UserIdInt int userHandle, @Nullable String accountName) { + public void setUserAccount(@UserIdInt int userId, @Nullable String accountName) { try { - mService.setUserAccount(userHandle, accountName); + mService.setUserAccount(userId, accountName); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } @@ -2477,20 +2477,19 @@ public class UserManager { } /** - * Returns list of the profiles of userHandle including - * userHandle itself. + * Returns list of the profiles of userId including userId itself. * Note that this returns both enabled and not enabled profiles. See * {@link #getEnabledProfiles(int)} if you need only the enabled ones. * * Requires {@link android.Manifest.permission#MANAGE_USERS} permission. - * @param userHandle profiles of this user will be returned. + * @param userId profiles of this user will be returned. * @return the list of profiles. * @hide */ @UnsupportedAppUsage - public List<UserInfo> getProfiles(@UserIdInt int userHandle) { + public List<UserInfo> getProfiles(@UserIdInt int userId) { try { - return mService.getProfiles(userHandle, false /* enabledOnly */); + return mService.getProfiles(userId, false /* enabledOnly */); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } @@ -2512,19 +2511,18 @@ public class UserManager { } /** - * Returns list of the profiles of userHandle including - * userHandle itself. + * Returns list of the profiles of userId including userId itself. * Note that this returns only enabled. * * Requires {@link android.Manifest.permission#MANAGE_USERS} permission. - * @param userHandle profiles of this user will be returned. + * @param userId profiles of this user will be returned. * @return the list of profiles. * @hide */ @UnsupportedAppUsage - public List<UserInfo> getEnabledProfiles(@UserIdInt int userHandle) { + public List<UserInfo> getEnabledProfiles(@UserIdInt int userId) { try { - return mService.getProfiles(userHandle, true /* enabledOnly */); + return mService.getProfiles(userId, true /* enabledOnly */); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } @@ -2584,14 +2582,14 @@ public class UserManager { /** * Returns the device credential owner id of the profile from - * which this method is called, or userHandle if called from a user that + * which this method is called, or userId if called from a user that * is not a profile. * * @hide */ - public int getCredentialOwnerProfile(@UserIdInt int userHandle) { + public int getCredentialOwnerProfile(@UserIdInt int userId) { try { - return mService.getCredentialOwnerProfile(userHandle); + return mService.getCredentialOwnerProfile(userId); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } @@ -2604,9 +2602,9 @@ public class UserManager { * @hide */ @UnsupportedAppUsage - public UserInfo getProfileParent(@UserIdInt int userHandle) { + public UserInfo getProfileParent(@UserIdInt int userId) { try { - return mService.getProfileParent(userHandle); + return mService.getProfileParent(userId); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } @@ -2785,13 +2783,13 @@ public class UserManager { /** * Removes a user and all associated data. * Requires {@link android.Manifest.permission#MANAGE_USERS} permission. - * @param userHandle the integer handle of the user, where 0 is the primary user. + * @param userId the integer handle of the user. * @hide */ @UnsupportedAppUsage - public boolean removeUser(@UserIdInt int userHandle) { + public boolean removeUser(@UserIdInt int userId) { try { - return mService.removeUser(userHandle); + return mService.removeUser(userId); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } @@ -2823,9 +2821,9 @@ public class UserManager { * @see {@link #removeUser(int)} * @hide */ - public boolean removeUserEvenWhenDisallowed(@UserIdInt int userHandle) { + public boolean removeUserEvenWhenDisallowed(@UserIdInt int userId) { try { - return mService.removeUserEvenWhenDisallowed(userHandle); + return mService.removeUserEvenWhenDisallowed(userId); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } @@ -2835,13 +2833,13 @@ public class UserManager { * Updates the user's name. * Requires {@link android.Manifest.permission#MANAGE_USERS} permission. * - * @param userHandle the user's integer handle + * @param userId the user's integer id * @param name the new name for the user * @hide */ - public void setUserName(@UserIdInt int userHandle, String name) { + public void setUserName(@UserIdInt int userId, String name) { try { - mService.setUserName(userHandle, name); + mService.setUserName(userId, name); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } @@ -2862,13 +2860,13 @@ public class UserManager { /** * Sets the user's photo. - * @param userHandle the user for whom to change the photo. + * @param userId the user for whom to change the photo. * @param icon the bitmap to set as the photo. * @hide */ - public void setUserIcon(@UserIdInt int userHandle, Bitmap icon) { + public void setUserIcon(@UserIdInt int userId, Bitmap icon) { try { - mService.setUserIcon(userHandle, icon); + mService.setUserIcon(userId, icon); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } @@ -2889,15 +2887,15 @@ public class UserManager { /** * Returns a file descriptor for the user's photo. PNG data can be read from this file. - * @param userHandle the user whose photo we want to read. + * @param userId the user whose photo we want to read. * @return a {@link Bitmap} of the user's photo, or null if there's no photo. * @see com.android.internal.util.UserIcons#getDefaultUserIcon for a default. * @hide */ @UnsupportedAppUsage - public Bitmap getUserIcon(@UserIdInt int userHandle) { + public Bitmap getUserIcon(@UserIdInt int userId) { try { - ParcelFileDescriptor fd = mService.getUserIcon(userHandle); + ParcelFileDescriptor fd = mService.getUserIcon(userId); if (fd != null) { try { return BitmapFactory.decodeFileDescriptor(fd.getFileDescriptor()); @@ -2999,27 +2997,27 @@ public class UserManager { } /** - * Returns a serial number on this device for a given userHandle. User handles can be recycled + * Returns a serial number on this device for a given userId. User handles can be recycled * when deleting and creating users, but serial numbers are not reused until the device is wiped. - * @param userHandle - * @return a serial number associated with that user, or -1 if the userHandle is not valid. + * @param userId + * @return a serial number associated with that user, or -1 if the userId is not valid. * @hide */ @UnsupportedAppUsage - public int getUserSerialNumber(@UserIdInt int userHandle) { + public int getUserSerialNumber(@UserIdInt int userId) { try { - return mService.getUserSerialNumber(userHandle); + return mService.getUserSerialNumber(userId); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } } /** - * Returns a userHandle on this device for a given user serial number. User handles can be + * Returns a userId on this device for a given user serial number. User handles can be * recycled when deleting and creating users, but serial numbers are not reused until the device * is wiped. * @param userSerialNumber - * @return the userHandle associated with that user serial number, or -1 if the serial number + * @return the userId associated with that user serial number, or -1 if the serial number * is not valid. * @hide */ diff --git a/core/java/android/os/UserManagerInternal.java b/core/java/android/os/UserManagerInternal.java index ddd949d64a1f..a5b71f64668d 100644 --- a/core/java/android/os/UserManagerInternal.java +++ b/core/java/android/os/UserManagerInternal.java @@ -137,7 +137,7 @@ public abstract class UserManagerInternal { String[] disallowedPackages); /** - * Same as {@link UserManager#removeUser(int userHandle)}, but bypasses the check for + * Same as {@link UserManager#removeUser(int userId)}, but bypasses the check for * {@link UserManager#DISALLOW_REMOVE_USER} and * {@link UserManager#DISALLOW_REMOVE_MANAGED_PROFILE} and does not require the * {@link android.Manifest.permission#MANAGE_USERS} permission. diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 522ad642ef0d..d5559aaa7146 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -105,8 +105,6 @@ public final class SurfaceControl implements Parcelable { long relativeToObject, int zorder); private static native void nativeSetPosition(long transactionObj, long nativeObject, float x, float y); - private static native void nativeSetGeometryAppliesWithResize(long transactionObj, - long nativeObject); private static native void nativeSetSize(long transactionObj, long nativeObject, int w, int h); private static native void nativeSetTransparentRegionHint(long transactionObj, long nativeObject, Region region); @@ -1104,16 +1102,6 @@ public final class SurfaceControl implements Parcelable { /** * @hide */ - public void setGeometryAppliesWithResize() { - checkNotReleased(); - synchronized(SurfaceControl.class) { - sGlobalTransaction.setGeometryAppliesWithResize(this); - } - } - - /** - * @hide - */ public void setBufferSize(int w, int h) { checkNotReleased(); synchronized(SurfaceControl.class) { @@ -2488,20 +2476,6 @@ public final class SurfaceControl implements Parcelable { } /** - * If the buffer size changes in this transaction, position and crop updates specified - * in this transaction will not complete until a buffer of the new size - * arrives. As transform matrix and size are already frozen in this fashion, - * this enables totally freezing the surface until the resize has completed - * (at which point the geometry influencing aspects of this transaction will then occur) - * @hide - */ - public Transaction setGeometryAppliesWithResize(SurfaceControl sc) { - sc.checkNotReleased(); - nativeSetGeometryAppliesWithResize(mNativeObject, sc.mNativeObject); - return this; - } - - /** * Sets the security of the surface. Setting the flag is equivalent to creating the * Surface with the {@link #SECURE} flag. * @hide diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index faeecda7250c..de77aaae1653 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -493,6 +493,9 @@ public class ChooserActivity extends ResolverActivity { @Override protected void onCreate(Bundle savedInstanceState) { final long intentReceivedTime = System.currentTimeMillis(); + // This is the only place this value is being set. Effectively final. + mIsAppPredictorComponentAvailable = isAppPredictionServiceAvailable(); + mIsSuccessfullySelected = false; Intent intent = getIntent(); Parcelable targetParcelable = intent.getParcelableExtra(Intent.EXTRA_INTENT); @@ -619,9 +622,6 @@ public class ChooserActivity extends ResolverActivity { .addTaggedData(MetricsEvent.FIELD_SHARESHEET_MIMETYPE, target.getType()) .addTaggedData(MetricsEvent.FIELD_TIME_TO_APP_TARGETS, systemCost)); - // This is the only place this value is being set. Effectively final. - mIsAppPredictorComponentAvailable = isAppPredictionServiceAvailable(); - AppPredictor appPredictor = getAppPredictorForDirectShareIfEnabled(); if (appPredictor != null) { mDirectShareAppTargetCache = new HashMap<>(); diff --git a/core/java/com/android/internal/util/ScreenshotHelper.java b/core/java/com/android/internal/util/ScreenshotHelper.java index 7fd94c6859fb..cac691cf7d45 100644 --- a/core/java/com/android/internal/util/ScreenshotHelper.java +++ b/core/java/com/android/internal/util/ScreenshotHelper.java @@ -1,6 +1,7 @@ package com.android.internal.util; import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -13,6 +14,8 @@ import android.os.RemoteException; import android.os.UserHandle; import android.util.Log; +import java.util.function.Consumer; + public class ScreenshotHelper { private static final String TAG = "ScreenshotHelper"; @@ -34,17 +37,58 @@ public class ScreenshotHelper { } /** + * Request a screenshot be taken with a specific timeout. + * + * Added to support reducing unit test duration; the method variant without a timeout argument + * is recommended for general use. + * + * @param screenshotType The type of screenshot, for example either + * {@link android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN} + * or + * {@link android.view.WindowManager.TAKE_SCREENSHOT_SELECTED_REGION} + * @param hasStatus {@code true} if the status bar is currently showing. {@code false} + * if + * not. + * @param hasNav {@code true} if the navigation bar is currently showing. {@code + * false} + * if not. + * @param handler A handler used in case the screenshot times out + * @param completionConsumer Consumes `false` if a screenshot was not taken, and `true` if the + * screenshot was taken. + */ + public void takeScreenshot(final int screenshotType, final boolean hasStatus, + final boolean hasNav, @NonNull Handler handler, + @Nullable Consumer<Boolean> completionConsumer) { + takeScreenshot(screenshotType, hasStatus, hasNav, SCREENSHOT_TIMEOUT_MS, handler, + completionConsumer); + } + + /** * Request a screenshot be taken. * - * @param screenshotType The type of screenshot, for example either - * {@link android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN} - * or {@link android.view.WindowManager.TAKE_SCREENSHOT_SELECTED_REGION} - * @param hasStatus {@code true} if the status bar is currently showing. {@code false} if not. - * @param hasNav {@code true} if the navigation bar is currently showing. {@code false} if not. - * @param handler A handler used in case the screenshot times out + * Added to support reducing unit test duration; the method variant without a timeout argument + * is recommended for general use. + * + * @param screenshotType The type of screenshot, for example either + * {@link android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN} + * or + * {@link android.view.WindowManager.TAKE_SCREENSHOT_SELECTED_REGION} + * @param hasStatus {@code true} if the status bar is currently showing. {@code false} + * if + * not. + * @param hasNav {@code true} if the navigation bar is currently showing. {@code + * false} + * if not. + * @param timeoutMs If the screenshot hasn't been completed within this time period, + * the screenshot attempt will be cancelled and `completionConsumer` + * will be run. + * @param handler A handler used in case the screenshot times out + * @param completionConsumer Consumes `false` if a screenshot was not taken, and `true` if the + * screenshot was taken. */ public void takeScreenshot(final int screenshotType, final boolean hasStatus, - final boolean hasNav, @NonNull Handler handler) { + final boolean hasNav, long timeoutMs, @NonNull Handler handler, + @Nullable Consumer<Boolean> completionConsumer) { synchronized (mScreenshotLock) { if (mScreenshotConnection != null) { return; @@ -54,7 +98,8 @@ public class ScreenshotHelper { final Intent serviceIntent = new Intent(); final Runnable mScreenshotTimeout = new Runnable() { - @Override public void run() { + @Override + public void run() { synchronized (mScreenshotLock) { if (mScreenshotConnection != null) { mContext.unbindService(mScreenshotConnection); @@ -62,6 +107,9 @@ public class ScreenshotHelper { notifyScreenshotError(); } } + if (completionConsumer != null) { + completionConsumer.accept(false); + } } }; @@ -86,15 +134,22 @@ public class ScreenshotHelper { handler.removeCallbacks(mScreenshotTimeout); } } + if (completionConsumer != null) { + completionConsumer.accept(true); + } } }; msg.replyTo = new Messenger(h); - msg.arg1 = hasStatus ? 1: 0; - msg.arg2 = hasNav ? 1: 0; + msg.arg1 = hasStatus ? 1 : 0; + msg.arg2 = hasNav ? 1 : 0; + try { messenger.send(msg); } catch (RemoteException e) { Log.e(TAG, "Couldn't take screenshot: " + e); + if (completionConsumer != null) { + completionConsumer.accept(false); + } } } } @@ -115,7 +170,7 @@ public class ScreenshotHelper { Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE, UserHandle.CURRENT)) { mScreenshotConnection = conn; - handler.postDelayed(mScreenshotTimeout, SCREENSHOT_TIMEOUT_MS); + handler.postDelayed(mScreenshotTimeout, timeoutMs); } } } diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index 67f52f464efa..bf0f10eb50c6 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -397,15 +397,6 @@ static void nativeSetGeometry(JNIEnv* env, jclass clazz, jlong transactionObj, j transaction->setGeometry(ctrl, source, dst, orientation); } -static void nativeSetGeometryAppliesWithResize(JNIEnv* env, jclass clazz, -jlong transactionObj, - jlong nativeObject) { - auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj); - - SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject); - transaction->setGeometryAppliesWithResize(ctrl); -} - static void nativeSetSize(JNIEnv* env, jclass clazz, jlong transactionObj, jlong nativeObject, jint w, jint h) { auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj); @@ -1295,8 +1286,6 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeSetRelativeLayer }, {"nativeSetPosition", "(JJFF)V", (void*)nativeSetPosition }, - {"nativeSetGeometryAppliesWithResize", "(JJ)V", - (void*)nativeSetGeometryAppliesWithResize }, {"nativeSetSize", "(JJII)V", (void*)nativeSetSize }, {"nativeSetTransparentRegionHint", "(JJLandroid/graphics/Region;)V", diff --git a/core/proto/Android.bp b/core/proto/Android.bp index 3b891d6b4947..e199dab181e0 100644 --- a/core/proto/Android.bp +++ b/core/proto/Android.bp @@ -28,3 +28,13 @@ cc_library_static { "android/bluetooth/smp/enums.proto", ], } + +java_library_host { + name: "windowmanager-log-proto", + srcs: [ + "android/server/windowmanagerlog.proto" + ], + proto: { + type: "full", + }, +} diff --git a/core/proto/android/server/windowmanagerlog.proto b/core/proto/android/server/windowmanagerlog.proto new file mode 100644 index 000000000000..5bee1bd670fc --- /dev/null +++ b/core/proto/android/server/windowmanagerlog.proto @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; + +package com.android.server.wm; + +option java_multiple_files = true; + +/* represents a single log entry */ +message ProtoLogMessage { + /* log statement identifier, created from message string and log level. */ + optional fixed32 message_hash = 1; + /* log time, relative to the elapsed system time clock. */ + optional fixed64 elapsed_realtime_nanos = 2; + /* string parameters passed to the log call. */ + repeated string str_params = 3; + /* integer parameters passed to the log call. */ + repeated sint64 sint64_params = 4 [packed=true]; + /* floating point parameters passed to the log call. */ + repeated double double_params = 5 [packed=true]; + /* boolean parameters passed to the log call. */ + repeated bool boolean_params = 6 [packed=true]; +} + +/* represents a log file containing window manager log entries. + Encoded, it should start with 0x9 0x57 0x49 0x4e 0x44 0x4f 0x4c 0x4f 0x47 (.WINDOLOG), such + that they can be easily identified. */ +message WindowManagerLogFileProto { + /* constant; MAGIC_NUMBER = (long) MAGIC_NUMBER_H << 32 | MagicNumber.MAGIC_NUMBER_L + (this is needed because enums have to be 32 bits and there's no nice way to put 64bit + constants into .proto files. */ + enum MagicNumber { + INVALID = 0; + MAGIC_NUMBER_L = 0x444e4957; /* WIND (little-endian ASCII) */ + MAGIC_NUMBER_H = 0x474f4c4f; /* OLOG (little-endian ASCII) */ + } + + /* the magic number header */ + optional fixed64 magic_number = 1; + /* log proto version. */ + optional string version = 2; + /* offset between real-time clock and elapsed system time clock in miliseconds. + Calculated as: (System.currentTimeMillis() - (SystemClock.elapsedRealtimeNanos() / 1000000) */ + optional fixed64 realTimeToElapsedTimeOffsetMillis = 3; + /* log entries */ + repeated ProtoLogMessage log = 4; +} diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml index b2f9183e4457..797d75f5a54f 100644 --- a/core/res/res/values-en-rCA/strings.xml +++ b/core/res/res/values-en-rCA/strings.xml @@ -1256,10 +1256,10 @@ <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Tap to see all networks"</string> <string name="wifi_available_action_connect" msgid="2635699628459488788">"Connect"</string> <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"All networks"</string> - <string name="wifi_suggestion_title" msgid="6396033039578436801">"Allow suggested Wi‑Fi networks?"</string> - <string name="wifi_suggestion_content" msgid="5603992011371520746">"<xliff:g id="NAME">%s</xliff:g> suggested networks. Device may connect automatically."</string> - <string name="wifi_suggestion_action_allow_app" msgid="7978995387498669901">"Allow"</string> - <string name="wifi_suggestion_action_disallow_app" msgid="6434097275967940372">"No, thanks"</string> + <string name="wifi_suggestion_title" msgid="9099832833531486167">"Connect to Wi‑Fi networks?"</string> + <string name="wifi_suggestion_content" msgid="5883181205841582873">"Suggested by <xliff:g id="NAME">%s</xliff:g>"</string> + <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Yes"</string> + <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"No"</string> <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi will turn on automatically"</string> <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"When you\'re near a high‑quality saved network"</string> <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Don\'t turn back on"</string> diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml index a6aa60430425..7ea76500e4ea 100644 --- a/core/res/res/values-en-rXC/strings.xml +++ b/core/res/res/values-en-rXC/strings.xml @@ -1256,10 +1256,10 @@ <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Tap to see all networks"</string> <string name="wifi_available_action_connect" msgid="2635699628459488788">"Connect"</string> <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"All networks"</string> - <string name="wifi_suggestion_title" msgid="6396033039578436801">"Allow suggested Wi‑Fi networks?"</string> - <string name="wifi_suggestion_content" msgid="5603992011371520746">"<xliff:g id="NAME">%s</xliff:g> suggested networks. Device may connect automatically."</string> - <string name="wifi_suggestion_action_allow_app" msgid="7978995387498669901">"Allow"</string> - <string name="wifi_suggestion_action_disallow_app" msgid="6434097275967940372">"No thanks"</string> + <string name="wifi_suggestion_title" msgid="9099832833531486167">"Connect to Wi‑Fi networks?"</string> + <string name="wifi_suggestion_content" msgid="5883181205841582873">"Suggested by <xliff:g id="NAME">%s</xliff:g>"</string> + <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"Yes"</string> + <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"No"</string> <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi will turn on automatically"</string> <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"When you\'re near a high quality saved network"</string> <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Don\'t turn back on"</string> diff --git a/core/tests/screenshothelpertests/Android.bp b/core/tests/screenshothelpertests/Android.bp new file mode 100644 index 000000000000..3d54b68b7ddc --- /dev/null +++ b/core/tests/screenshothelpertests/Android.bp @@ -0,0 +1,28 @@ +android_test { + name: "ScreenshotHelperTests", + + srcs: [ + "src/**/*.java", + ], + + static_libs: [ + "frameworks-base-testutils", + "androidx.test.runner", + "androidx.test.rules", + "androidx.test.ext.junit", + "mockito-target-minus-junit4", + "platform-test-annotations", + ], + + libs: [ + "android.test.runner", + "android.test.base", + "android.test.mock", + ], + + platform_apis: true, + test_suites: ["device-tests"], + + certificate: "platform", +} + diff --git a/core/tests/screenshothelpertests/AndroidManifest.xml b/core/tests/screenshothelpertests/AndroidManifest.xml new file mode 100644 index 000000000000..2e12ef4db358 --- /dev/null +++ b/core/tests/screenshothelpertests/AndroidManifest.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + android:installLocation="internalOnly" + package="com.android.internal.util" + android:sharedUserId="android.uid.systemui" > + + <application > + <uses-library android:name="android.test.runner" /> + </application> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.internal.util" + android:label="Screenshot Helper Tests" /> + + <protected-broadcast android:name="android.intent.action.USER_PRESENT" /> + +</manifest> diff --git a/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java b/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java new file mode 100644 index 000000000000..848364584ef3 --- /dev/null +++ b/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java @@ -0,0 +1,98 @@ +/* + * 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.internal.util; + +import static android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN; +import static android.view.WindowManager.TAKE_SCREENSHOT_SELECTED_REGION; + +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.fail; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; + +import android.content.Context; +import android.os.Handler; +import android.os.Looper; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +@RunWith(AndroidJUnit4.class) +public final class ScreenshotHelperTest { + private Context mContext; + private ScreenshotHelper mScreenshotHelper; + private Handler mHandler; + + + @Before + public void setUp() { + // `ScreenshotHelper.notifyScreenshotError()` calls `Context.sendBroadcastAsUser()` and + // `Context.bindServiceAsUser`. + // + // This raises a `SecurityException` if the device is locked. Calling either `Context` + // method results in a broadcast of `android.intent.action. USER_PRESENT`. Only the system + // process is allowed to broadcast that `Intent`. + mContext = Mockito.spy(Context.class); + Mockito.doNothing().when(mContext).sendBroadcastAsUser(any(), any()); + Mockito.doReturn(true).when(mContext).bindServiceAsUser(any(), any(), anyInt(), any()); + + mHandler = new Handler(Looper.getMainLooper()); + mScreenshotHelper = new ScreenshotHelper(mContext); + } + + @Test + public void testFullscreenScreenshot() { + mScreenshotHelper.takeScreenshot(TAKE_SCREENSHOT_FULLSCREEN, false, false, mHandler, null); + } + + @Test + public void testSelectedRegionScreenshot() { + mScreenshotHelper.takeScreenshot(TAKE_SCREENSHOT_SELECTED_REGION, false, false, mHandler, + null); + } + + @Test + public void testScreenshotTimesOut() { + long timeoutMs = 10; + + CountDownLatch lock = new CountDownLatch(1); + mScreenshotHelper.takeScreenshot(TAKE_SCREENSHOT_FULLSCREEN, false, false, timeoutMs, + mHandler, + worked -> { + assertFalse(worked); + lock.countDown(); + }); + + try { + // Add tolerance for delay to prevent flakes. + long awaitDurationMs = timeoutMs + 100; + if (!lock.await(awaitDurationMs, TimeUnit.MILLISECONDS)) { + fail("lock never freed"); + } + } catch (InterruptedException e) { + fail("lock interrupted"); + } + } +} diff --git a/libs/hwui/service/GraphicsStatsService.cpp b/libs/hwui/service/GraphicsStatsService.cpp index 8b5912b2081a..12c5b836f711 100644 --- a/libs/hwui/service/GraphicsStatsService.cpp +++ b/libs/hwui/service/GraphicsStatsService.cpp @@ -282,9 +282,9 @@ void dumpAsTextToFd(protos::GraphicsStatsProto* proto, int fd) { return; } dprintf(fd, "\nPackage: %s", proto->package_name().c_str()); - dprintf(fd, "\nVersion: %lld", proto->version_code()); - dprintf(fd, "\nStats since: %lldns", proto->stats_start()); - dprintf(fd, "\nStats end: %lldns", proto->stats_end()); + dprintf(fd, "\nVersion: %" PRId64, proto->version_code()); + dprintf(fd, "\nStats since: %" PRId64 "ns", proto->stats_start()); + dprintf(fd, "\nStats end: %" PRId64 "ns", proto->stats_end()); auto summary = proto->summary(); dprintf(fd, "\nTotal frames rendered: %d", summary.total_frames()); dprintf(fd, "\nJanky frames: %d (%.2f%%)", summary.janky_frames(), diff --git a/libs/protoutil/include/android/util/ProtoOutputStream.h b/libs/protoutil/include/android/util/ProtoOutputStream.h index a6af4757a140..42bf03e6de05 100644 --- a/libs/protoutil/include/android/util/ProtoOutputStream.h +++ b/libs/protoutil/include/android/util/ProtoOutputStream.h @@ -98,6 +98,7 @@ public: bool write(uint64_t fieldId, double val); bool write(uint64_t fieldId, float val); bool write(uint64_t fieldId, int val); + bool write(uint64_t fieldId, long val); bool write(uint64_t fieldId, long long val); bool write(uint64_t fieldId, bool val); bool write(uint64_t fieldId, std::string val); diff --git a/libs/protoutil/src/ProtoOutputStream.cpp b/libs/protoutil/src/ProtoOutputStream.cpp index 6cfa357b580b..ea9b79a0353f 100644 --- a/libs/protoutil/src/ProtoOutputStream.cpp +++ b/libs/protoutil/src/ProtoOutputStream.cpp @@ -116,6 +116,34 @@ ProtoOutputStream::write(uint64_t fieldId, int val) } bool +ProtoOutputStream::write(uint64_t fieldId, long val) +{ + if (mCompact) return false; + const uint32_t id = (uint32_t)fieldId; + switch (fieldId & FIELD_TYPE_MASK) { + case FIELD_TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break; + case FIELD_TYPE_FLOAT: writeFloatImpl(id, (float)val); break; + case FIELD_TYPE_INT64: writeInt64Impl(id, (long long)val); break; + case FIELD_TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break; + case FIELD_TYPE_INT32: writeInt32Impl(id, (int)val); break; + case FIELD_TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break; + case FIELD_TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break; + case FIELD_TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break; + case FIELD_TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break; + case FIELD_TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break; + case FIELD_TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break; + case FIELD_TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break; + case FIELD_TYPE_ENUM: writeEnumImpl(id, (int)val); break; + case FIELD_TYPE_BOOL: writeBoolImpl(id, val != 0); break; + default: + ALOGW("Field type %d is not supported when writing long val.", + (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT)); + return false; + } + return true; +} + +bool ProtoOutputStream::write(uint64_t fieldId, long long val) { return internalWrite(fieldId, val, "long long"); diff --git a/packages/BackupEncryption/Android.bp b/packages/BackupEncryption/Android.bp index 50dbcdb1b4c7..9bcd677538eb 100644 --- a/packages/BackupEncryption/Android.bp +++ b/packages/BackupEncryption/Android.bp @@ -17,8 +17,15 @@ android_app { name: "BackupEncryption", srcs: ["src/**/*.java"], + libs: ["backup-encryption-protos"], optimize: { enabled: false }, platform_apis: true, certificate: "platform", privileged: true, -}
\ No newline at end of file +} + +java_library { + name: "backup-encryption-protos", + proto: { type: "nano" }, + srcs: ["proto/**/*.proto"], +} diff --git a/packages/BackupEncryption/proto/wrapped_key.proto b/packages/BackupEncryption/proto/wrapped_key.proto new file mode 100644 index 000000000000..817b7b40d606 --- /dev/null +++ b/packages/BackupEncryption/proto/wrapped_key.proto @@ -0,0 +1,52 @@ +syntax = "proto2"; + +package android_backup_crypto; + +option java_package = "com.android.server.backup.encryption.protos"; +option java_outer_classname = "WrappedKeyProto"; + +// Metadata associated with a tertiary key. +message KeyMetadata { + // Type of Cipher algorithm the key is used for. + enum Type { + UNKNOWN = 0; + // No padding. Uses 12-byte nonce. Tag length 16 bytes. + AES_256_GCM = 1; + } + + // What kind of Cipher algorithm the key is used for. We assume at the moment + // that this will always be AES_256_GCM and throw if this is not the case. + // Provided here for forwards compatibility in case at some point we need to + // change Cipher algorithm. + optional Type type = 1; +} + +// An encrypted tertiary key. +message WrappedKey { + // The Cipher with which the key was encrypted. + enum WrapAlgorithm { + UNKNOWN = 0; + // No padding. Uses 16-byte nonce (see nonce field). Tag length 16 bytes. + // The nonce is 16-bytes as this is wrapped with a key in AndroidKeyStore. + // AndroidKeyStore requires that it generates the IV, and it generates a + // 16-byte IV for you. You CANNOT provide your own IV. + AES_256_GCM = 1; + } + + // Cipher algorithm used to wrap the key. We assume at the moment that this + // is always AES_256_GC and throw if this is not the case. Provided here for + // forwards compatibility if at some point we need to change Cipher algorithm. + optional WrapAlgorithm wrap_algorithm = 1; + + // The nonce used to initialize the Cipher in AES/256/GCM mode. + optional bytes nonce = 2; + + // The encrypted bytes of the key material. + optional bytes key = 3; + + // Associated key metadata. + optional KeyMetadata metadata = 4; + + // Deprecated field; Do not use + reserved 5; +} diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/KeyWrapUtils.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/KeyWrapUtils.java new file mode 100644 index 000000000000..a043c1fe687f --- /dev/null +++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/KeyWrapUtils.java @@ -0,0 +1,132 @@ +/* + * 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.backup.encryption.keys; + +import com.android.server.backup.encryption.protos.nano.WrappedKeyProto; + +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.Locale; + +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.SecretKey; +import javax.crypto.spec.GCMParameterSpec; + +/** Utility functions for wrapping and unwrapping tertiary keys. */ +public class KeyWrapUtils { + private static final String AES_GCM_MODE = "AES/GCM/NoPadding"; + private static final int GCM_TAG_LENGTH_BYTES = 16; + private static final int BITS_PER_BYTE = 8; + private static final int GCM_TAG_LENGTH_BITS = GCM_TAG_LENGTH_BYTES * BITS_PER_BYTE; + private static final String KEY_ALGORITHM = "AES"; + + /** + * Uses the secondary key to unwrap the wrapped tertiary key. + * + * @param secondaryKey The secondary key used to wrap the tertiary key. + * @param wrappedKey The wrapped tertiary key. + * @return The unwrapped tertiary key. + * @throws InvalidKeyException if the provided secondary key cannot unwrap the tertiary key. + */ + public static SecretKey unwrap(SecretKey secondaryKey, WrappedKeyProto.WrappedKey wrappedKey) + throws InvalidKeyException, NoSuchAlgorithmException, + InvalidAlgorithmParameterException, NoSuchPaddingException { + if (wrappedKey.wrapAlgorithm != WrappedKeyProto.WrappedKey.AES_256_GCM) { + throw new InvalidKeyException( + String.format( + Locale.US, + "Could not unwrap key wrapped with %s algorithm", + wrappedKey.wrapAlgorithm)); + } + + if (wrappedKey.metadata == null) { + throw new InvalidKeyException("Metadata missing from wrapped tertiary key."); + } + + if (wrappedKey.metadata.type != WrappedKeyProto.KeyMetadata.AES_256_GCM) { + throw new InvalidKeyException( + String.format( + Locale.US, + "Wrapped key was unexpected %s algorithm. Only support" + + " AES/GCM/NoPadding.", + wrappedKey.metadata.type)); + } + + Cipher cipher = getCipher(); + + cipher.init( + Cipher.UNWRAP_MODE, + secondaryKey, + new GCMParameterSpec(GCM_TAG_LENGTH_BITS, wrappedKey.nonce)); + + return (SecretKey) cipher.unwrap(wrappedKey.key, KEY_ALGORITHM, Cipher.SECRET_KEY); + } + + /** + * Wraps the tertiary key with the secondary key. + * + * @param secondaryKey The secondary key to use for wrapping. + * @param tertiaryKey The key to wrap. + * @return The wrapped key. + * @throws InvalidKeyException if the key is not good for wrapping. + * @throws IllegalBlockSizeException if there is an issue wrapping. + */ + public static WrappedKeyProto.WrappedKey wrap(SecretKey secondaryKey, SecretKey tertiaryKey) + throws InvalidKeyException, IllegalBlockSizeException, NoSuchAlgorithmException, + NoSuchPaddingException { + Cipher cipher = getCipher(); + cipher.init(Cipher.WRAP_MODE, secondaryKey); + + WrappedKeyProto.WrappedKey wrappedKey = new WrappedKeyProto.WrappedKey(); + wrappedKey.key = cipher.wrap(tertiaryKey); + wrappedKey.nonce = cipher.getIV(); + wrappedKey.wrapAlgorithm = WrappedKeyProto.WrappedKey.AES_256_GCM; + wrappedKey.metadata = new WrappedKeyProto.KeyMetadata(); + wrappedKey.metadata.type = WrappedKeyProto.KeyMetadata.AES_256_GCM; + return wrappedKey; + } + + /** + * Rewraps a tertiary key with a new secondary key. + * + * @param oldSecondaryKey The old secondary key, used to unwrap the tertiary key. + * @param newSecondaryKey The new secondary key, used to rewrap the tertiary key. + * @param tertiaryKey The tertiary key, wrapped by {@code oldSecondaryKey}. + * @return The tertiary key, wrapped by {@code newSecondaryKey}. + * @throws InvalidKeyException if the key is not good for wrapping or unwrapping. + * @throws IllegalBlockSizeException if there is an issue wrapping. + */ + public static WrappedKeyProto.WrappedKey rewrap( + SecretKey oldSecondaryKey, + SecretKey newSecondaryKey, + WrappedKeyProto.WrappedKey tertiaryKey) + throws InvalidKeyException, IllegalBlockSizeException, + InvalidAlgorithmParameterException, NoSuchAlgorithmException, + NoSuchPaddingException { + return wrap(newSecondaryKey, unwrap(oldSecondaryKey, tertiaryKey)); + } + + private static Cipher getCipher() throws NoSuchPaddingException, NoSuchAlgorithmException { + return Cipher.getInstance(AES_GCM_MODE); + } + + // Statics only + private KeyWrapUtils() {} +} diff --git a/packages/BackupEncryption/test/robolectric/Android.bp b/packages/BackupEncryption/test/robolectric/Android.bp index 6d1abbb61a8e..3376ec97e02f 100644 --- a/packages/BackupEncryption/test/robolectric/Android.bp +++ b/packages/BackupEncryption/test/robolectric/Android.bp @@ -20,6 +20,7 @@ android_robolectric_test { ], java_resource_dirs: ["config"], libs: [ + "backup-encryption-protos", "platform-test-annotations", "testng", ], diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/KeyWrapUtilsTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/KeyWrapUtilsTest.java new file mode 100644 index 000000000000..b60740421ad5 --- /dev/null +++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/KeyWrapUtilsTest.java @@ -0,0 +1,158 @@ +/* + * 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.backup.encryption.keys; + +import static com.android.server.backup.testing.CryptoTestUtils.generateAesKey; + +import static com.google.common.truth.Truth.assertThat; + +import android.platform.test.annotations.Presubmit; + +import com.android.server.backup.encryption.protos.nano.WrappedKeyProto; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; + +import java.security.InvalidKeyException; + +import javax.crypto.SecretKey; + +/** Key wrapping tests */ +@RunWith(RobolectricTestRunner.class) +@Presubmit +public class KeyWrapUtilsTest { + private static final int KEY_SIZE_BITS = 256; + private static final int BITS_PER_BYTE = 8; + private static final int GCM_NONCE_LENGTH_BYTES = 16; + private static final int GCM_TAG_LENGTH_BYTES = 16; + + /** Test a wrapped key has metadata */ + @Test + public void wrap_addsMetadata() throws Exception { + WrappedKeyProto.WrappedKey wrappedKey = + KeyWrapUtils.wrap( + /*secondaryKey=*/ generateAesKey(), /*tertiaryKey=*/ generateAesKey()); + assertThat(wrappedKey.metadata).isNotNull(); + assertThat(wrappedKey.metadata.type).isEqualTo(WrappedKeyProto.KeyMetadata.AES_256_GCM); + } + + /** Test a wrapped key has an algorithm specified */ + @Test + public void wrap_addsWrapAlgorithm() throws Exception { + WrappedKeyProto.WrappedKey wrappedKey = + KeyWrapUtils.wrap( + /*secondaryKey=*/ generateAesKey(), /*tertiaryKey=*/ generateAesKey()); + assertThat(wrappedKey.wrapAlgorithm).isEqualTo(WrappedKeyProto.WrappedKey.AES_256_GCM); + } + + /** Test a wrapped key haas an nonce of the right length */ + @Test + public void wrap_addsNonceOfAppropriateLength() throws Exception { + WrappedKeyProto.WrappedKey wrappedKey = + KeyWrapUtils.wrap( + /*secondaryKey=*/ generateAesKey(), /*tertiaryKey=*/ generateAesKey()); + assertThat(wrappedKey.nonce).hasLength(GCM_NONCE_LENGTH_BYTES); + } + + /** Test a wrapped key has a key of the right length */ + @Test + public void wrap_addsTagOfAppropriateLength() throws Exception { + WrappedKeyProto.WrappedKey wrappedKey = + KeyWrapUtils.wrap( + /*secondaryKey=*/ generateAesKey(), /*tertiaryKey=*/ generateAesKey()); + assertThat(wrappedKey.key).hasLength(KEY_SIZE_BITS / BITS_PER_BYTE + GCM_TAG_LENGTH_BYTES); + } + + /** Ensure a key can be wrapped and unwrapped again */ + @Test + public void unwrap_unwrapsEncryptedKey() throws Exception { + SecretKey secondaryKey = generateAesKey(); + SecretKey tertiaryKey = generateAesKey(); + WrappedKeyProto.WrappedKey wrappedKey = KeyWrapUtils.wrap(secondaryKey, tertiaryKey); + SecretKey unwrappedKey = KeyWrapUtils.unwrap(secondaryKey, wrappedKey); + assertThat(unwrappedKey).isEqualTo(tertiaryKey); + } + + /** Ensure the unwrap method rejects keys with bad algorithms */ + @Test(expected = InvalidKeyException.class) + public void unwrap_throwsForBadWrapAlgorithm() throws Exception { + SecretKey secondaryKey = generateAesKey(); + WrappedKeyProto.WrappedKey wrappedKey = KeyWrapUtils.wrap(secondaryKey, generateAesKey()); + wrappedKey.wrapAlgorithm = WrappedKeyProto.WrappedKey.UNKNOWN; + + KeyWrapUtils.unwrap(secondaryKey, wrappedKey); + } + + /** Ensure the unwrap method rejects metadata indicating the encryption type is unknown */ + @Test(expected = InvalidKeyException.class) + public void unwrap_throwsForBadKeyAlgorithm() throws Exception { + SecretKey secondaryKey = generateAesKey(); + WrappedKeyProto.WrappedKey wrappedKey = KeyWrapUtils.wrap(secondaryKey, generateAesKey()); + wrappedKey.metadata.type = WrappedKeyProto.KeyMetadata.UNKNOWN; + + KeyWrapUtils.unwrap(secondaryKey, wrappedKey); + } + + /** Ensure the unwrap method rejects wrapped keys missing the metadata */ + @Test(expected = InvalidKeyException.class) + public void unwrap_throwsForMissingMetadata() throws Exception { + SecretKey secondaryKey = generateAesKey(); + WrappedKeyProto.WrappedKey wrappedKey = KeyWrapUtils.wrap(secondaryKey, generateAesKey()); + wrappedKey.metadata = null; + + KeyWrapUtils.unwrap(secondaryKey, wrappedKey); + } + + /** Ensure unwrap rejects invalid secondary keys */ + @Test(expected = InvalidKeyException.class) + public void unwrap_throwsForBadSecondaryKey() throws Exception { + WrappedKeyProto.WrappedKey wrappedKey = + KeyWrapUtils.wrap( + /*secondaryKey=*/ generateAesKey(), /*tertiaryKey=*/ generateAesKey()); + + KeyWrapUtils.unwrap(generateAesKey(), wrappedKey); + } + + /** Ensure rewrap can rewrap keys */ + @Test + public void rewrap_canBeUnwrappedWithNewSecondaryKey() throws Exception { + SecretKey tertiaryKey = generateAesKey(); + SecretKey oldSecondaryKey = generateAesKey(); + SecretKey newSecondaryKey = generateAesKey(); + WrappedKeyProto.WrappedKey wrappedWithOld = KeyWrapUtils.wrap(oldSecondaryKey, tertiaryKey); + + WrappedKeyProto.WrappedKey wrappedWithNew = + KeyWrapUtils.rewrap(oldSecondaryKey, newSecondaryKey, wrappedWithOld); + + assertThat(KeyWrapUtils.unwrap(newSecondaryKey, wrappedWithNew)).isEqualTo(tertiaryKey); + } + + /** Ensure rewrap doesn't create something decryptable by an old key */ + @Test(expected = InvalidKeyException.class) + public void rewrap_cannotBeUnwrappedWithOldSecondaryKey() throws Exception { + SecretKey tertiaryKey = generateAesKey(); + SecretKey oldSecondaryKey = generateAesKey(); + SecretKey newSecondaryKey = generateAesKey(); + WrappedKeyProto.WrappedKey wrappedWithOld = KeyWrapUtils.wrap(oldSecondaryKey, tertiaryKey); + + WrappedKeyProto.WrappedKey wrappedWithNew = + KeyWrapUtils.rewrap(oldSecondaryKey, newSecondaryKey, wrappedWithOld); + + KeyWrapUtils.unwrap(oldSecondaryKey, wrappedWithNew); + } +} diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml index e591121d4b59..83636dc19beb 100644 --- a/packages/SettingsLib/res/values-te/strings.xml +++ b/packages/SettingsLib/res/values-te/strings.xml @@ -143,7 +143,7 @@ <string name="data_usage_ota" msgid="5377889154805560860">"సిస్టమ్ అప్డేట్లు"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB టీథరింగ్"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"పోర్టబుల్ హాట్స్పాట్"</string> - <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"బ్లూటూత్ టెథెరింగ్"</string> + <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"బ్లూటూత్ టీథరింగ్"</string> <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"టీథరింగ్"</string> <string name="tether_settings_title_all" msgid="8356136101061143841">"టీథరింగ్ & పోర్టబుల్ హాట్స్పాట్"</string> <string name="managed_user_title" msgid="8109605045406748842">"అన్ని కార్యాలయ అనువర్తనాలు"</string> @@ -376,7 +376,7 @@ <string name="daltonizer_mode_protanomaly" msgid="8424148009038666065">"ప్రొటానోమలీ (ఎరుపు-ఆకుపచ్చ రంగు)"</string> <string name="daltonizer_mode_tritanomaly" msgid="481725854987912389">"ట్రైటనోమలీ (నీలం-పసుపు రంగు)"</string> <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"రంగు సవరణ"</string> - <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"ఈ ఫీచర్ ప్రయోగాత్మకమైనది, పనితీరుపై ప్రభావం చూపవచ్చు."</string> + <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"ఈ లక్షణం ప్రయోగాత్మకమైనది మరియు పనితీరుపై ప్రభావం చూపవచ్చు."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> ద్వారా భర్తీ చేయబడింది"</string> <string name="power_remaining_settings_home_page" msgid="4845022416859002011">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="6123167166221295462">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> సమయం మిగిలి ఉంది"</string> @@ -414,7 +414,7 @@ <string name="disabled" msgid="9206776641295849915">"నిలిపివేయబడింది"</string> <string name="external_source_trusted" msgid="2707996266575928037">"అనుమతించినవి"</string> <string name="external_source_untrusted" msgid="2677442511837596726">"అనుమతించబడలేదు"</string> - <string name="install_other_apps" msgid="6986686991775883017">"తెలియని యాప్లను ఇన్స్టాల్ చేయడం"</string> + <string name="install_other_apps" msgid="6986686991775883017">"తెలియని యాప్లను ఇన్స్టాల్ చేయండి"</string> <string name="home" msgid="3256884684164448244">"సెట్టింగ్ల హోమ్"</string> <string-array name="battery_labels"> <item msgid="8494684293649631252">"0%"</item> diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml index 0d3e8de0559d..5dca4759a629 100644 --- a/packages/SettingsLib/res/values-uz/strings.xml +++ b/packages/SettingsLib/res/values-uz/strings.xml @@ -201,7 +201,7 @@ <string name="vpn_settings_not_available" msgid="956841430176985598">"Ushbu foydalanuvchi uchun VPN sozlamalari mavjud emas"</string> <string name="tethering_settings_not_available" msgid="6765770438438291012">"Ushbu foydalanuvchi uchun Modem rejimi sozlamalari mavjud emas"</string> <string name="apn_settings_not_available" msgid="7873729032165324000">"Ushbu foydalanuvchi uchun Internetga kirish nuqtasi (APN) sozlamalari mavjud emas"</string> - <string name="enable_adb" msgid="7982306934419797485">"USB orqali nosozliklarni aniqlash"</string> + <string name="enable_adb" msgid="7982306934419797485">"USB orqali nosozliklarni tuzatish"</string> <string name="enable_adb_summary" msgid="4881186971746056635">"USB orqali kompyuterga ulanganda tuzatish rejimi yoqilsin"</string> <string name="clear_adb_keys" msgid="4038889221503122743">"USB orqali nosozliklarni tuzatishni taqiqlash"</string> <string name="bugreport_in_power" msgid="7923901846375587241">"Xatoliklar hisoboti"</string> @@ -264,7 +264,7 @@ <string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Mobil internet har doim yoniq tursin, hatto Wi-Fi yoniq bo‘lsa ham (bir tarmoqdan ikkinchisiga tezroq o‘tish uchun)."</string> <string name="tethering_hardware_offload_summary" msgid="7726082075333346982">"Modem rejimida apparatli tezlashtirishdan foydalanish (agar mavjud bo‘lsa)"</string> <string name="adb_warning_title" msgid="6234463310896563253">"USB orqali nosozliklarni tuzatishga ruxsat berilsinmi?"</string> - <string name="adb_warning_message" msgid="7316799925425402244">"USB orqali nosozliklarni aniqlash faqat dasturlash maqsadlarida yoqiladi. Undan maʼlumotlarni qurilmangiz va kompyuter o‘rtasida ko‘chirish, ilovalarni xabarnomasiz o‘rnatish va jurnal maʼlumotlarini o‘qish uchun foydalaniladi."</string> + <string name="adb_warning_message" msgid="7316799925425402244">"USB orqali nosozliklarni tuzatish faqat dasturlash maqsadlarida yoqiladi. Undan ma‘lumotlarni qurilmangiz va kompyuter o‘rtasida ko‘chirish, ilovalarni xabarnomasiz o‘rnatish va jurnal ma‘lumotlarini o‘qish uchun foydalaniladi."</string> <string name="adb_keys_warning_message" msgid="5659849457135841625">"USB orqali nosozliklarni tuzatishga berilgan ruxsat siz hisobingizga kirgan barcha kompyuterlar uchun bekor qilinsinmi?"</string> <string name="dev_settings_warning_title" msgid="7244607768088540165">"Dasturlash sozlamalariga ruxsat berilsinmi?"</string> <string name="dev_settings_warning_message" msgid="2298337781139097964">"Bu sozlamalar faqat dasturlash maqsadlariga mo‘ljallangan. Shuning uchun, ular qurilmangizga va undagi ilovalariga shikast yetkazib, noto‘g‘ri ishlashiga sabab bo‘lishi mumkin."</string> diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml index c64130210d37..e85199f9ca0d 100644 --- a/packages/SettingsLib/res/values-zh-rTW/strings.xml +++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml @@ -329,7 +329,7 @@ <string name="show_all_anrs" msgid="4924885492787069007">"顯示背景 ANR"</string> <string name="show_all_anrs_summary" msgid="6636514318275139826">"為背景應用程式顯示「應用程式無回應」對話方塊"</string> <string name="show_notification_channel_warnings" msgid="1399948193466922683">"顯示通知管道警告"</string> - <string name="show_notification_channel_warnings_summary" msgid="5536803251863694895">"當應用程式未經有效管道發布通知時,在畫面上顯示警告"</string> + <string name="show_notification_channel_warnings_summary" msgid="5536803251863694895">"當應用程式未經有效管道發佈通知時,在畫面上顯示警告"</string> <string name="force_allow_on_external" msgid="3215759785081916381">"強制允許將應用程式寫入外部儲存空間"</string> <string name="force_allow_on_external_summary" msgid="3640752408258034689">"允許將任何應用程式寫入外部儲存空間 (無論資訊清單值為何)"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"將活動強制設為可調整大小"</string> diff --git a/packages/Shell/res/values-zh-rTW/strings.xml b/packages/Shell/res/values-zh-rTW/strings.xml index 96671c8c0175..fc6397dc2013 100644 --- a/packages/Shell/res/values-zh-rTW/strings.xml +++ b/packages/Shell/res/values-zh-rTW/strings.xml @@ -25,9 +25,9 @@ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"系統即將在手機上顯示錯誤報告"</string> <string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"選取即可分享錯誤報告"</string> <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"輕觸即可分享錯誤報告"</string> - <string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"選取即可分享不包含螢幕截圖的錯誤報告;你也可以等候螢幕畫面擷取完畢"</string> - <string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"輕觸即可分享無螢幕截圖的錯誤報告;你也可以等候螢幕畫面擷取完畢"</string> - <string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"輕觸即可分享無螢幕截圖的錯誤報告;你也可以等候螢幕畫面擷取完畢"</string> + <string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"選取即可分享不包含螢幕擷取畫面的錯誤報告;你也可以等候螢幕畫面擷取完畢"</string> + <string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"輕觸即可分享無螢幕擷圖的錯誤報告;你也可以等候螢幕畫面擷取完畢"</string> + <string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"輕觸即可分享無螢幕擷圖的錯誤報告;你也可以等候螢幕畫面擷取完畢"</string> <string name="bugreport_confirm" msgid="5917407234515812495">"錯誤報告的資料來自系統的各種記錄檔,當中可能包含敏感資料 (例如應用程式使用情形和位置資料)。請務必只與你信任的使用者和應用程式分享錯誤報告。"</string> <string name="bugreport_confirm_dont_repeat" msgid="6179945398364357318">"不要再顯示"</string> <string name="bugreport_storage_title" msgid="5332488144740527109">"錯誤報告"</string> @@ -35,9 +35,9 @@ <string name="bugreport_add_details_to_zip_failed" msgid="1302931926486712371">"無法在 ZIP 檔案中加入錯誤報告"</string> <string name="bugreport_unnamed" msgid="2800582406842092709">"未命名"</string> <string name="bugreport_info_action" msgid="2158204228510576227">"詳細資料"</string> - <string name="bugreport_screenshot_action" msgid="8677781721940614995">"螢幕截圖"</string> - <string name="bugreport_screenshot_taken" msgid="5684211273096253120">"已成功拍攝螢幕截圖。"</string> - <string name="bugreport_screenshot_failed" msgid="5853049140806834601">"無法拍攝螢幕截圖。"</string> + <string name="bugreport_screenshot_action" msgid="8677781721940614995">"螢幕擷取畫面"</string> + <string name="bugreport_screenshot_taken" msgid="5684211273096253120">"已成功拍攝螢幕擷取畫面。"</string> + <string name="bugreport_screenshot_failed" msgid="5853049140806834601">"無法拍攝螢幕擷取畫面。"</string> <string name="bugreport_info_dialog_title" msgid="1355948594292983332">"錯誤報告 <xliff:g id="ID">#%d</xliff:g> 的詳細資料"</string> <string name="bugreport_info_name" msgid="4414036021935139527">"檔案名稱"</string> <string name="bugreport_info_title" msgid="2306030793918239804">"錯誤標題"</string> diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml index 938cb0dd3b12..9903cc04e97a 100644 --- a/packages/SystemUI/res/values-en-rCA/strings.xml +++ b/packages/SystemUI/res/values-en-rCA/strings.xml @@ -401,7 +401,6 @@ <string name="speed_bump_explanation" msgid="1288875699658819755">"Less urgent notifications below"</string> <string name="notification_tap_again" msgid="7590196980943943842">"Tap again to open"</string> <string name="keyguard_unlock" msgid="6035822649218712063">"Swipe up to open"</string> - <string name="keyguard_retry" msgid="5221600879614948709">"Swipe up to try again"</string> <string name="do_disclosure_generic" msgid="5615898451805157556">"This device is managed by your organisation"</string> <string name="do_disclosure_with_name" msgid="5640615509915445501">"This device is managed by <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string> <string name="phone_hint" msgid="4872890986869209950">"Swipe from icon for phone"</string> @@ -545,7 +544,6 @@ <string name="screen_pinning_description_recents_invisible_accessible" msgid="6134833683151189507">"This keeps it in view until you unpin. Touch & hold Home to unpin."</string> <string name="screen_pinning_toast" msgid="2266705122951934150">"To unpin this screen, touch & hold Back and Overview buttons"</string> <string name="screen_pinning_toast_recents_invisible" msgid="8252402309499161281">"To unpin this screen, touch & hold Back and Home buttons"</string> - <string name="screen_pinning_toast_gesture_nav" msgid="5070548776081664958">"To unpin this screen, swipe up & hold"</string> <string name="screen_pinning_positive" msgid="3783985798366751226">"Got it"</string> <string name="screen_pinning_negative" msgid="3741602308343880268">"No, thanks"</string> <string name="screen_pinning_start" msgid="1022122128489278317">"Screen pinned"</string> diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml index 139215aa196f..d8c862affe08 100644 --- a/packages/SystemUI/res/values-en-rXC/strings.xml +++ b/packages/SystemUI/res/values-en-rXC/strings.xml @@ -401,7 +401,6 @@ <string name="speed_bump_explanation" msgid="1288875699658819755">"Less urgent notifications below"</string> <string name="notification_tap_again" msgid="7590196980943943842">"Tap again to open"</string> <string name="keyguard_unlock" msgid="6035822649218712063">"Swipe up to open"</string> - <string name="keyguard_retry" msgid="5221600879614948709">"Swipe up to try again"</string> <string name="do_disclosure_generic" msgid="5615898451805157556">"This device is managed by your organization"</string> <string name="do_disclosure_with_name" msgid="5640615509915445501">"This device is managed by <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string> <string name="phone_hint" msgid="4872890986869209950">"Swipe from icon for phone"</string> @@ -545,7 +544,6 @@ <string name="screen_pinning_description_recents_invisible_accessible" msgid="6134833683151189507">"This keeps it in view until you unpin. Touch & hold Home to unpin."</string> <string name="screen_pinning_toast" msgid="2266705122951934150">"To unpin this screen, touch & hold Back and Overview buttons"</string> <string name="screen_pinning_toast_recents_invisible" msgid="8252402309499161281">"To unpin this screen, touch & hold Back and Home buttons"</string> - <string name="screen_pinning_toast_gesture_nav" msgid="5070548776081664958">"To unpin this screen, swipe up & hold"</string> <string name="screen_pinning_positive" msgid="3783985798366751226">"Got it"</string> <string name="screen_pinning_negative" msgid="3741602308343880268">"No thanks"</string> <string name="screen_pinning_start" msgid="1022122128489278317">"Screen pinned"</string> diff --git a/packages/SystemUI/src/com/android/systemui/CornerHandleView.java b/packages/SystemUI/src/com/android/systemui/CornerHandleView.java index a94952c5bc19..6209c2c36206 100644 --- a/packages/SystemUI/src/com/android/systemui/CornerHandleView.java +++ b/packages/SystemUI/src/com/android/systemui/CornerHandleView.java @@ -19,6 +19,7 @@ package com.android.systemui; import android.animation.ArgbEvaluator; import android.content.Context; import android.graphics.Canvas; +import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; import android.graphics.RectF; @@ -27,6 +28,7 @@ import android.util.DisplayMetrics; import android.view.ContextThemeWrapper; import android.view.View; +import com.android.internal.graphics.ColorUtils; import com.android.settingslib.Utils; /** @@ -107,6 +109,7 @@ public class CornerHandleView extends View { mPaint.setColor((int) ArgbEvaluator.getInstance().evaluate(darkIntensity, mLightColor, mDarkColor)); + updateShadow(); if (getVisibility() == VISIBLE) { invalidate(); } @@ -118,6 +121,21 @@ public class CornerHandleView extends View { canvas.drawPath(mPath, mPaint); } + private void updateShadow() { + if (ColorUtils.calculateLuminance(mPaint.getColor()) > 0.7f) { + mPaint.setShadowLayer(/** radius */ 5,/** shadowDx */ 0, /** shadowDy */ -1, + /** color */ ColorUtils.setAlphaComponent(/** color */ Color.BLACK, + /** alpha */ 102)); + } else { + mPaint.setShadowLayer(/** radius */ 0, /** shadowDx */ 0, /** shadowDy */ 0, + /** color */ Color.TRANSPARENT); + } + + if (getVisibility() == VISIBLE) { + invalidate(); + } + } + private static float convertDpToPixel(float dp, Context context) { return dp * ((float) context.getResources().getDisplayMetrics().densityDpi / DisplayMetrics.DENSITY_DEFAULT); diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java index 9cf738541276..5dd3cb1d8bdc 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java @@ -383,6 +383,15 @@ final class AssistHandleReminderExpBehavior implements BehaviorController { mIsLearned = mLearningCount >= getLearningCount() || mLearningTimeElapsed >= getLearningTimeMs(); + + mHandler.post(this::recordLearnTimeElapsed); + } + + private void recordLearnTimeElapsed() { + if (mContext != null) { + Settings.Secure.putLong( + mContext.getContentResolver(), LEARNING_TIME_ELAPSED_KEY, mLearningTimeElapsed); + } } private void resetConsecutiveTaskSwitches() { diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java index e42eb0a19f74..eb60b3db7a11 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java @@ -21,6 +21,7 @@ import android.os.AsyncTask; import android.os.Binder; import android.os.Bundle; import android.os.Handler; +import android.os.HandlerThread; import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; @@ -163,8 +164,10 @@ public class AssistManager implements ConfigurationChangedReceiver { mAssistUtils = new AssistUtils(context); mAssistDisclosure = new AssistDisclosure(context, new Handler()); mPhoneStateMonitor = new PhoneStateMonitor(context); - mHandleController = - new AssistHandleBehaviorController(context, mAssistUtils, new Handler()); + final HandlerThread assistHandleThread = new HandlerThread("AssistHandleThread"); + assistHandleThread.start(); + mHandleController = new AssistHandleBehaviorController( + context, mAssistUtils, assistHandleThread.getThreadHandler()); registerVoiceInteractionSessionListener(); mInterestingConfigChanges = new InterestingConfigChanges(ActivityInfo.CONFIG_ORIENTATION diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java index 3f598ffad709..c9c6a0c6868b 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -614,7 +614,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, mHandler.postDelayed(new Runnable() { @Override public void run() { - mScreenshotHelper.takeScreenshot(1, true, true, mHandler); + mScreenshotHelper.takeScreenshot(1, true, true, mHandler, null); MetricsLogger.action(mContext, MetricsEvent.ACTION_SCREENSHOT_POWER_MENU); } diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java index 7b22a49fc88a..29606347f009 100644 --- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java +++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java @@ -111,7 +111,12 @@ public class ImageWallpaperRenderer implements GLWallpaperRenderer, mBitmap = mWallpaperManager.getBitmap(); mWallpaperManager.forgetLoadedWallpaper(); if (mBitmap != null) { - mSurfaceSize.set(0, 0, mBitmap.getWidth(), mBitmap.getHeight()); + float scale = (float) mScissor.height() / mBitmap.getHeight(); + int surfaceHeight = Math.max(mScissor.height(), mBitmap.getHeight()); + int surfaceWidth = scale > 1f + ? Math.round(mBitmap.getWidth() * scale) + : mBitmap.getWidth(); + mSurfaceSize.set(0, 0, surfaceWidth, surfaceHeight); } } return mBitmap != null; diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java index 17402905b21a..3be3422a36ad 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java @@ -16,6 +16,9 @@ package com.android.systemui.pip.phone; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; + import android.app.ActivityManager; import android.app.ActivityTaskManager; import android.app.IActivityManager; @@ -182,7 +185,6 @@ public class PipManager implements BasePipManager { ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener); mInputConsumerController = InputConsumerController.getPipInputConsumer(); - mInputConsumerController.registerInputConsumer(); mMediaController = new PipMediaController(context, mActivityManager); mMenuController = new PipMenuActivityController(context, mActivityManager, mMediaController, mInputConsumerController); @@ -190,6 +192,18 @@ public class PipManager implements BasePipManager { mMenuController, mInputConsumerController); mAppOpsListener = new PipAppOpsListener(context, mActivityManager, mTouchHandler.getMotionHelper()); + + // If SystemUI restart, and it already existed a pinned stack, + // register the pip input consumer to ensure touch can send to it. + try { + ActivityManager.StackInfo stackInfo = mActivityTaskManager.getStackInfo( + WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED); + if (stackInfo != null) { + mInputConsumerController.registerInputConsumer(); + } + } catch (RemoteException e) { + e.printStackTrace(); + } } /** diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java index 21f58128322d..4f2a6d82a08e 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java @@ -48,7 +48,6 @@ import android.content.ComponentName; import android.content.Intent; import android.content.pm.ParceledListSlice; import android.graphics.Color; -import android.graphics.PointF; import android.graphics.Rect; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; @@ -64,7 +63,6 @@ import android.util.Pair; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; -import android.view.ViewConfiguration; import android.view.ViewGroup; import android.view.WindowManager.LayoutParams; import android.view.accessibility.AccessibilityManager; @@ -92,6 +90,7 @@ public class PipMenuActivity extends Activity { public static final int MESSAGE_UPDATE_ACTIONS = 4; public static final int MESSAGE_UPDATE_DISMISS_FRACTION = 5; public static final int MESSAGE_ANIMATION_ENDED = 6; + public static final int MESSAGE_TOUCH_EVENT = 7; private static final int INITIAL_DISMISS_DELAY = 3500; private static final int POST_INTERACTION_DISMISS_DELAY = 2000; @@ -128,10 +127,6 @@ public class PipMenuActivity extends Activity { } }; - private PipTouchState mTouchState; - private PointF mDownPosition = new PointF(); - private PointF mDownDelta = new PointF(); - private ViewConfiguration mViewConfig; private Handler mHandler = new Handler(); private Messenger mToControllerMessenger; private Messenger mMessenger = new Messenger(new Handler() { @@ -169,6 +164,12 @@ public class PipMenuActivity extends Activity { mAllowTouches = true; break; } + + case MESSAGE_TOUCH_EVENT: { + final MotionEvent ev = (MotionEvent) msg.obj; + dispatchTouchEvent(ev); + break; + } } } }); @@ -184,15 +185,7 @@ public class PipMenuActivity extends Activity { protected void onCreate(@Nullable Bundle savedInstanceState) { // Set the flags to allow us to watch for outside touches and also hide the menu and start // manipulating the PIP in the same touch gesture - mViewConfig = ViewConfiguration.get(this); - mTouchState = new PipTouchState(mViewConfig, mHandler, () -> { - if (mMenuState == MENU_STATE_CLOSE) { - showPipMenu(); - } else { - expandPip(); - } - }); - getWindow().addFlags(LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH | LayoutParams.FLAG_SLIPPERY); + getWindow().addFlags(LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH); super.onCreate(savedInstanceState); setContentView(R.layout.pip_menu_activity); @@ -204,32 +197,6 @@ public class PipMenuActivity extends Activity { mViewRoot.setBackground(mBackgroundDrawable); mMenuContainer = findViewById(R.id.menu_container); mMenuContainer.setAlpha(0); - mMenuContainer.setOnTouchListener((v, event) -> { - mTouchState.onTouchEvent(event); - switch (event.getAction()) { - case MotionEvent.ACTION_UP: - if (mTouchState.isDoubleTap() || mMenuState == MENU_STATE_FULL) { - // Expand to fullscreen if this is a double tap or we are already expanded - expandPip(); - } else if (!mTouchState.isWaitingForDoubleTap()) { - // User has stalled long enough for this not to be a drag or a double tap, - // just expand the menu if necessary - if (mMenuState == MENU_STATE_CLOSE) { - showPipMenu(); - } - } else { - // Next touch event _may_ be the second tap for the double-tap, schedule a - // fallback runnable to trigger the menu if no touch event occurs before the - // next tap - mTouchState.scheduleDoubleTapTimeoutCallback(); - } - // Fall through - case MotionEvent.ACTION_CANCEL: - mTouchState.reset(); - break; - } - return true; - }); mSettingsButton = findViewById(R.id.settings); mSettingsButton.setAlpha(0); mSettingsButton.setOnClickListener((v) -> { @@ -240,7 +207,11 @@ public class PipMenuActivity extends Activity { mDismissButton = findViewById(R.id.dismiss); mDismissButton.setAlpha(0); mDismissButton.setOnClickListener(v -> dismissPip()); - findViewById(R.id.expand_button).setOnClickListener(v -> expandPip()); + findViewById(R.id.expand_button).setOnClickListener(v -> { + if (mMenuContainer.getAlpha() != 0) { + expandPip(); + } + }); mActionsGroup = findViewById(R.id.actions_group); mBetweenActionPaddingLand = getResources().getDimensionPixelSize( R.dimen.pip_between_action_padding_land); @@ -298,27 +269,14 @@ public class PipMenuActivity extends Activity { @Override public boolean dispatchTouchEvent(MotionEvent ev) { if (!mAllowTouches) { - return super.dispatchTouchEvent(ev); + return false; } // On the first action outside the window, hide the menu switch (ev.getAction()) { case MotionEvent.ACTION_OUTSIDE: hideMenu(); - break; - case MotionEvent.ACTION_DOWN: - mDownPosition.set(ev.getX(), ev.getY()); - mDownDelta.set(0f, 0f); - break; - case MotionEvent.ACTION_MOVE: - mDownDelta.set(ev.getX() - mDownPosition.x, ev.getY() - mDownPosition.y); - if (mDownDelta.length() > mViewConfig.getScaledTouchSlop() - && mMenuState != MENU_STATE_NONE) { - // Restore the input consumer and let that drive the movement of this menu - notifyRegisterInputConsumer(); - cancelDelayedFinish(); - } - break; + return true; } return super.dispatchTouchEvent(ev); } @@ -381,7 +339,6 @@ public class PipMenuActivity extends Activity { if (allowMenuTimeout) { repostDelayedFinish(POST_INTERACTION_DISMISS_DELAY); } - notifyUnregisterInputConsumer(); } } @@ -506,11 +463,13 @@ public class PipMenuActivity extends Activity { actionView.setContentDescription(action.getContentDescription()); if (action.isEnabled()) { actionView.setOnClickListener(v -> { - try { - action.getActionIntent().send(); - } catch (CanceledException e) { - Log.w(TAG, "Failed to send action", e); - } + mHandler.post(() -> { + try { + action.getActionIntent().send(); + } catch (CanceledException e) { + Log.w(TAG, "Failed to send action", e); + } + }); }); } actionView.setEnabled(action.isEnabled()); @@ -554,18 +513,6 @@ public class PipMenuActivity extends Activity { mBackgroundDrawable.setAlpha(alpha); } - private void notifyRegisterInputConsumer() { - Message m = Message.obtain(); - m.what = PipMenuActivityController.MESSAGE_REGISTER_INPUT_CONSUMER; - sendMessage(m, "Could not notify controller to register input consumer"); - } - - private void notifyUnregisterInputConsumer() { - Message m = Message.obtain(); - m.what = PipMenuActivityController.MESSAGE_UNREGISTER_INPUT_CONSUMER; - sendMessage(m, "Could not notify controller to unregister input consumer"); - } - private void notifyMenuStateChange(int menuState) { mMenuState = menuState; Message m = Message.obtain(); @@ -583,11 +530,6 @@ public class PipMenuActivity extends Activity { }, false /* notifyMenuVisibility */, false /* isDismissing */); } - private void minimizePip() { - sendEmptyMessage(PipMenuActivityController.MESSAGE_MINIMIZE_PIP, - "Could not notify controller to minimize PIP"); - } - private void dismissPip() { // Do not notify menu visibility when hiding the menu, the controller will do this when it // handles the message @@ -597,12 +539,6 @@ public class PipMenuActivity extends Activity { }, false /* notifyMenuVisibility */, true /* isDismissing */); } - private void showPipMenu() { - Message m = Message.obtain(); - m.what = PipMenuActivityController.MESSAGE_SHOW_MENU; - sendMessage(m, "Could not notify controller to show PIP menu"); - } - private void showSettings() { final Pair<ComponentName, Integer> topPipActivityInfo = PipUtils.getTopPinnedActivity(this, ActivityManager.getService()); diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java index 14459d6b8111..62c59e5842ff 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java @@ -37,6 +37,7 @@ import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; import android.util.Log; +import android.view.MotionEvent; import com.android.systemui.pip.phone.PipMediaController.ActionListener; import com.android.systemui.shared.system.InputConsumerController; @@ -156,14 +157,6 @@ public class PipMenuActivityController { mListeners.forEach(l -> l.onPipShowMenu()); break; } - case MESSAGE_REGISTER_INPUT_CONSUMER: { - mInputConsumerController.registerInputConsumer(); - break; - } - case MESSAGE_UNREGISTER_INPUT_CONSUMER: { - mInputConsumerController.unregisterInputConsumer(); - break; - } case MESSAGE_UPDATE_ACTIVITY_CALLBACK: { mToActivityMessenger = msg.replyTo; setStartActivityRequested(false); @@ -212,15 +205,12 @@ public class PipMenuActivityController { } public void onActivityPinned() { - if (mMenuState == MENU_STATE_NONE) { - // If the menu is not visible, then re-register the input consumer if it is not already - // registered - mInputConsumerController.registerInputConsumer(); - } + mInputConsumerController.registerInputConsumer(); } public void onActivityUnpinned() { hideMenu(); + mInputConsumerController.unregisterInputConsumer(); setStartActivityRequested(false); } @@ -495,11 +485,7 @@ public class PipMenuActivityController { Log.d(TAG, "onMenuStateChanged() mMenuState=" + mMenuState + " menuState=" + menuState + " resize=" + resize); } - if (menuState == MENU_STATE_NONE) { - mInputConsumerController.registerInputConsumer(); - } else { - mInputConsumerController.unregisterInputConsumer(); - } + if (menuState != mMenuState) { mListeners.forEach(l -> l.onPipMenuStateChanged(menuState, resize)); if (menuState == MENU_STATE_FULL) { @@ -521,6 +507,22 @@ public class PipMenuActivityController { mStartActivityRequestedTime = requested ? SystemClock.uptimeMillis() : 0; } + /** + * Handles touch event sent from pip input consumer. + */ + void handleTouchEvent(MotionEvent ev) { + if (mToActivityMessenger != null) { + Message m = Message.obtain(); + m.what = PipMenuActivity.MESSAGE_TOUCH_EVENT; + m.obj = ev; + try { + mToActivityMessenger.send(m); + } catch (RemoteException e) { + Log.e(TAG, "Could not dispatch touch event", e); + } + } + } + public void dump(PrintWriter pw, String prefix) { final String innerPrefix = prefix + " "; pw.println(prefix + TAG); diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java index b05058a92650..30cf412671bc 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java @@ -363,6 +363,8 @@ public class PipTouchHandler { // Update the touch state mTouchState.onTouchEvent(ev); + boolean shouldDeliverToMenu = mMenuState != MENU_STATE_NONE; + switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: { mMotionHelper.synchronizePinnedStackBounds(); @@ -378,6 +380,8 @@ public class PipTouchHandler { break; } } + + shouldDeliverToMenu = !mTouchState.isDragging(); break; } case MotionEvent.ACTION_UP: { @@ -394,6 +398,7 @@ public class PipTouchHandler { // Fall through to clean up } case MotionEvent.ACTION_CANCEL: { + shouldDeliverToMenu = !mTouchState.startedDragging() && !mTouchState.isDragging(); mTouchState.reset(); break; } @@ -425,7 +430,20 @@ public class PipTouchHandler { break; } } - return mMenuState == MENU_STATE_NONE; + + // Deliver the event to PipMenuActivity to handle button click if the menu has shown. + if (shouldDeliverToMenu) { + final MotionEvent cloneEvent = MotionEvent.obtain(ev); + // Send the cancel event and cancel menu timeout if it starts to drag. + if (mTouchState.startedDragging()) { + cloneEvent.setAction(MotionEvent.ACTION_CANCEL); + mMenuController.pokeMenu(); + } + + mMenuController.handleTouchEvent(cloneEvent); + } + + return true; } /** @@ -741,11 +759,11 @@ public class PipTouchHandler { mMotionHelper.animateToClosestSnapTarget(mMovementBounds, null /* updateListener */, null /* animatorListener */); setMinimizedStateInternal(false); + } else if (mTouchState.isDoubleTap()) { + // Expand to fullscreen if this is a double tap + mMotionHelper.expandPip(); } else if (mMenuState != MENU_STATE_FULL) { - if (mTouchState.isDoubleTap()) { - // Expand to fullscreen if this is a double tap - mMotionHelper.expandPip(); - } else if (!mTouchState.isWaitingForDoubleTap()) { + if (!mTouchState.isWaitingForDoubleTap()) { // User has stalled long enough for this not to be a drag or a double tap, just // expand the menu mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(), @@ -756,9 +774,6 @@ public class PipTouchHandler { // next tap mTouchState.scheduleDoubleTapTimeoutCallback(); } - } else { - mMenuController.hideMenu(); - mMotionHelper.expandPip(); } return true; } diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java index 69efbc8575e0..e3f65ef812fb 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java @@ -106,6 +106,7 @@ public class PipTouchState { mIsDoubleTap = !mPreviouslyDragging && (mDownTouchTime - mLastDownTouchTime) < DOUBLE_TAP_TIMEOUT; mIsWaitingForDoubleTap = false; + mIsDragging = false; mLastDownTouchTime = mDownTouchTime; if (mDoubleTapTimeoutCallback != null) { mHandler.removeCallbacks(mDoubleTapTimeoutCallback); diff --git a/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values-ru/strings.xml b/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values-ru/strings.xml index 0ff85fe39c99..dc77981e6c95 100644 --- a/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values-ru/strings.xml +++ b/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values-ru/strings.xml @@ -17,5 +17,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="display_cutout_emulation_overlay" msgid="1677693377327336341">"В правом углу"</string> + <string name="display_cutout_emulation_overlay" msgid="1677693377327336341">"Сделать вырез в углу"</string> </resources> diff --git a/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values-ru/strings.xml b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values-ru/strings.xml index 2493da34af1f..a02eaf7bdc26 100644 --- a/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values-ru/strings.xml +++ b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values-ru/strings.xml @@ -17,5 +17,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="display_cutout_emulation_overlay" msgid="5323179900047630217">"Сверху и снизу"</string> + <string name="display_cutout_emulation_overlay" msgid="5323179900047630217">"Увеличить вырез вдвое"</string> </resources> diff --git a/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values-ru/strings.xml b/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values-ru/strings.xml index 89ac1c3e1b11..1d1656d2862e 100644 --- a/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values-ru/strings.xml +++ b/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values-ru/strings.xml @@ -17,5 +17,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="display_cutout_emulation_overlay" msgid="6424539415439220018">"Сверху"</string> + <string name="display_cutout_emulation_overlay" msgid="6424539415439220018">"Сделать вырез выше"</string> </resources> diff --git a/services/accessibility/java/com/android/server/accessibility/GlobalActionPerformer.java b/services/accessibility/java/com/android/server/accessibility/GlobalActionPerformer.java index 672518cc17ed..b9b2654b93cc 100644 --- a/services/accessibility/java/com/android/server/accessibility/GlobalActionPerformer.java +++ b/services/accessibility/java/com/android/server/accessibility/GlobalActionPerformer.java @@ -24,10 +24,7 @@ import android.os.Binder; import android.os.Handler; import android.os.Looper; import android.os.PowerManager; -import android.os.RemoteException; -import android.os.ServiceManager; import android.os.SystemClock; -import android.view.IWindowManager; import android.view.InputDevice; import android.view.KeyCharacterMap; import android.view.KeyEvent; @@ -191,7 +188,7 @@ public class GlobalActionPerformer { ScreenshotHelper screenshotHelper = (mScreenshotHelperSupplier != null) ? mScreenshotHelperSupplier.get() : new ScreenshotHelper(mContext); screenshotHelper.takeScreenshot(android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN, - true, true, new Handler(Looper.getMainLooper())); + true, true, new Handler(Looper.getMainLooper()), null); return true; } } diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java index 5672a13991d8..3fbb21e1bb48 100644 --- a/services/core/java/com/android/server/PackageWatchdog.java +++ b/services/core/java/com/android/server/PackageWatchdog.java @@ -865,8 +865,8 @@ public class PackageWatchdog { MonitoredPackage p = it.next(); int oldState = p.getHealthCheckStateLocked(); int newState = p.handleElapsedTimeLocked(elapsedMs); - if (oldState != MonitoredPackage.STATE_FAILED - && newState == MonitoredPackage.STATE_FAILED) { + if (oldState != HealthCheckState.FAILED + && newState == HealthCheckState.FAILED) { Slog.i(TAG, "Package " + p.mName + " failed health check"); failedPackages.add(p); } @@ -941,6 +941,23 @@ public class PackageWatchdog { } } + @Retention(SOURCE) + @IntDef(value = { + HealthCheckState.ACTIVE, + HealthCheckState.INACTIVE, + HealthCheckState.PASSED, + HealthCheckState.FAILED}) + public @interface HealthCheckState { + // The package has not passed health check but has requested a health check + int ACTIVE = 0; + // The package has not passed health check and has not requested a health check + int INACTIVE = 1; + // The package has passed health check + int PASSED = 2; + // The package has failed health check + int FAILED = 3; + } + /** * Represents a package and its health check state along with the time * it should be monitored for. @@ -949,23 +966,12 @@ public class PackageWatchdog { * instances of this class. */ class MonitoredPackage { - // Health check states - // TODO(b/120598832): Prefix with HEALTH_CHECK - // mName has not passed health check but has requested a health check - public static final int STATE_ACTIVE = 0; - // mName has not passed health check and has not requested a health check - public static final int STATE_INACTIVE = 1; - // mName has passed health check - public static final int STATE_PASSED = 2; - // mName has failed health check - public static final int STATE_FAILED = 3; - //TODO(b/120598832): VersionedPackage? private final String mName; // One of STATE_[ACTIVE|INACTIVE|PASSED|FAILED]. Updated on construction and after // methods that could change the health check state: handleElapsedTimeLocked and // tryPassHealthCheckLocked - private int mHealthCheckState = STATE_INACTIVE; + private int mHealthCheckState = HealthCheckState.INACTIVE; // Whether an explicit health check has passed. // This value in addition with mHealthCheckDurationMs determines the health check state // of the package, see #getHealthCheckStateLocked @@ -1052,7 +1058,7 @@ public class PackageWatchdog { + ". Using total duration " + mDurationMs + "ms instead"); initialHealthCheckDurationMs = mDurationMs; } - if (mHealthCheckState == STATE_INACTIVE) { + if (mHealthCheckState == HealthCheckState.INACTIVE) { // Transitions to ACTIVE mHealthCheckDurationMs = initialHealthCheckDurationMs; } @@ -1072,7 +1078,7 @@ public class PackageWatchdog { } // Transitions to FAILED if now <= 0 and health check not passed mDurationMs -= elapsedMs; - if (mHealthCheckState == STATE_ACTIVE) { + if (mHealthCheckState == HealthCheckState.ACTIVE) { // We only update health check durations if we have #setHealthCheckActiveLocked // This ensures we don't leave the INACTIVE state for an unexpected elapsed time // Transitions to FAILED if now <= 0 and health check not passed @@ -1082,14 +1088,15 @@ public class PackageWatchdog { } /** - * Marks the health check as passed and transitions to {@link #STATE_PASSED} - * if not yet {@link #STATE_FAILED}. + * Marks the health check as passed and transitions to {@link HealthCheckState.PASSED} + * if not yet {@link HealthCheckState.FAILED}. * - * @return the new health check state + * @return the new {@link HealthCheckState health check state} */ @GuardedBy("mLock") + @HealthCheckState public int tryPassHealthCheckLocked() { - if (mHealthCheckState != STATE_FAILED) { + if (mHealthCheckState != HealthCheckState.FAILED) { // FAILED is a final state so only pass if we haven't failed // Transition to PASSED mHasPassedHealthCheck = true; @@ -1102,12 +1109,11 @@ public class PackageWatchdog { return mName; } - //TODO(b/120598832): IntDef /** - * Returns the current health check state, any of {@link #STATE_ACTIVE}, - * {@link #STATE_INACTIVE} or {@link #STATE_PASSED} + * Returns the current {@link HealthCheckState health check state}. */ @GuardedBy("mLock") + @HealthCheckState public int getHealthCheckStateLocked() { return mHealthCheckState; } @@ -1140,28 +1146,30 @@ public class PackageWatchdog { */ @GuardedBy("mLock") public boolean isPendingHealthChecksLocked() { - return mHealthCheckState == STATE_ACTIVE || mHealthCheckState == STATE_INACTIVE; + return mHealthCheckState == HealthCheckState.ACTIVE + || mHealthCheckState == HealthCheckState.INACTIVE; } /** * Updates the health check state based on {@link #mHasPassedHealthCheck} * and {@link #mHealthCheckDurationMs}. * - * @return the new health check state + * @return the new {@link HealthCheckState health check state} */ @GuardedBy("mLock") + @HealthCheckState private int updateHealthCheckStateLocked() { int oldState = mHealthCheckState; if (mHasPassedHealthCheck) { // Set final state first to avoid ambiguity - mHealthCheckState = STATE_PASSED; + mHealthCheckState = HealthCheckState.PASSED; } else if (mHealthCheckDurationMs <= 0 || mDurationMs <= 0) { // Set final state first to avoid ambiguity - mHealthCheckState = STATE_FAILED; + mHealthCheckState = HealthCheckState.FAILED; } else if (mHealthCheckDurationMs == Long.MAX_VALUE) { - mHealthCheckState = STATE_INACTIVE; + mHealthCheckState = HealthCheckState.INACTIVE; } else { - mHealthCheckState = STATE_ACTIVE; + mHealthCheckState = HealthCheckState.ACTIVE; } Slog.i(TAG, "Updated health check state for package " + mName + ": " + toString(oldState) + " -> " + toString(mHealthCheckState)); @@ -1169,15 +1177,15 @@ public class PackageWatchdog { } /** Returns a {@link String} representation of the current health check state. */ - private String toString(int state) { + private String toString(@HealthCheckState int state) { switch (state) { - case STATE_ACTIVE: + case HealthCheckState.ACTIVE: return "ACTIVE"; - case STATE_INACTIVE: + case HealthCheckState.INACTIVE: return "INACTIVE"; - case STATE_PASSED: + case HealthCheckState.PASSED: return "PASSED"; - case STATE_FAILED: + case HealthCheckState.FAILED: return "FAILED"; default: return "UNKNOWN"; diff --git a/services/core/java/com/android/server/SystemService.java b/services/core/java/com/android/server/SystemService.java index 4151c7269934..5e659b64dbbe 100644 --- a/services/core/java/com/android/server/SystemService.java +++ b/services/core/java/com/android/server/SystemService.java @@ -168,7 +168,7 @@ public abstract class SystemService { * calls this method). */ @Deprecated - public void onStartUser(@UserIdInt int userHandle) {} + public void onStartUser(@UserIdInt int userId) {} /** * Called when a new user is starting, for system services to initialize any per-user @@ -189,7 +189,7 @@ public abstract class SystemService { * default calls this method). */ @Deprecated - public void onUnlockUser(@UserIdInt int userHandle) {} + public void onUnlockUser(@UserIdInt int userId) {} /** * Called when an existing user is in the process of being unlocked. This @@ -218,7 +218,7 @@ public abstract class SystemService { * (which by default calls this method). */ @Deprecated - public void onSwitchUser(@UserIdInt int userHandle) {} + public void onSwitchUser(@UserIdInt int userId) {} /** * Called when switching to a different foreground user, for system services that have @@ -243,7 +243,7 @@ public abstract class SystemService { * calls this method). */ @Deprecated - public void onStopUser(@UserIdInt int userHandle) {} + public void onStopUser(@UserIdInt int userId) {} /** * Called when an existing user is stopping, for system services to finalize any per-user @@ -268,7 +268,7 @@ public abstract class SystemService { * default calls this method). */ @Deprecated - public void onCleanupUser(@UserIdInt int userHandle) {} + public void onCleanupUser(@UserIdInt int userId) {} /** * Called when an existing user is stopping, for system services to finalize any per-user diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index fbd77577b4bf..09bfb7a1adca 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -6098,16 +6098,16 @@ public class ActivityManagerService extends IActivityManager.Stub return ptw != null ? ptw.tag : null; } - private ProviderInfo getProviderInfoLocked(String authority, int userHandle, int pmFlags) { + private ProviderInfo getProviderInfoLocked(String authority, @UserIdInt int userId, + int pmFlags) { ProviderInfo pi = null; - ContentProviderRecord cpr = mProviderMap.getProviderByName(authority, userHandle); + ContentProviderRecord cpr = mProviderMap.getProviderByName(authority, userId); if (cpr != null) { pi = cpr.info; } else { try { pi = AppGlobals.getPackageManager().resolveContentProvider( - authority, PackageManager.GET_URI_PERMISSION_PATTERNS | pmFlags, - userHandle); + authority, PackageManager.GET_URI_PERMISSION_PATTERNS | pmFlags, userId); } catch (RemoteException ex) { } } @@ -9163,7 +9163,16 @@ public class ActivityManagerService extends IActivityManager.Stub Integer.toString(currentUserId), currentUserId); mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_USER_FOREGROUND_START, Integer.toString(currentUserId), currentUserId); - mSystemServiceManager.startUser(t, currentUserId); + + // On Automotive, at this point the system user has already been started and unlocked, + // and some of the tasks we do here have already been done. So skip those in that case. + // TODO(b/132262830): this workdound shouldn't be necessary once we move the + // headless-user start logic to UserManager-land + final boolean bootingSystemUser = currentUserId == UserHandle.USER_SYSTEM; + + if (bootingSystemUser) { + mSystemServiceManager.startUser(t, currentUserId); + } synchronized (this) { // Only start up encryption-aware persistent apps; once user is @@ -9192,12 +9201,6 @@ public class ActivityManagerService extends IActivityManager.Stub t.traceEnd(); } - // On Automotive, at this point the system user has already been started and unlocked, - // and some of the tasks we do here have already been done. So skip those in that case. - // TODO(b/132262830): this workdound shouldn't be necessary once we move the - // headless-user start logic to UserManager-land - final boolean bootingSystemUser = currentUserId == UserHandle.USER_SYSTEM; - if (bootingSystemUser) { t.traceBegin("startHomeOnAllDisplays"); mAtmInternal.startHomeOnAllDisplays(currentUserId, "systemReady"); @@ -15338,11 +15341,11 @@ public class ActivityManagerService extends IActivityManager.Stub intent.getAction()); final String[] packageNames = intent.getStringArrayExtra( Intent.EXTRA_CHANGED_PACKAGE_LIST); - final int userHandle = intent.getIntExtra( + final int userIdExtra = intent.getIntExtra( Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); mAtmInternal.onPackagesSuspendedChanged(packageNames, suspended, - userHandle); + userIdExtra); break; } break; @@ -18061,7 +18064,7 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override - public void killForegroundAppsForUser(int userHandle) { + public void killForegroundAppsForUser(@UserIdInt int userId) { synchronized (ActivityManagerService.this) { final ArrayList<ProcessRecord> procs = new ArrayList<>(); final int NP = mProcessList.mProcessNames.getMap().size(); @@ -18076,7 +18079,7 @@ public class ActivityManagerService extends IActivityManager.Stub continue; } if (app.removed - || (app.userId == userHandle && app.hasForegroundActivities())) { + || (app.userId == userId && app.hasForegroundActivities())) { procs.add(app); } } diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index 5fe72ddfaf91..5c8e530faf70 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -841,7 +841,7 @@ class UserController implements Handler.Callback { * @return user id to lock. UserHandler.USER_NULL will be returned if no user should be locked. */ @GuardedBy("mLock") - private int updateUserToLockLU(int userId) { + private int updateUserToLockLU(@UserIdInt int userId) { int userIdToLock = userId; if (mDelayUserDataLocking && !getUserInfo(userId).isEphemeral() && !hasUserRestriction(UserManager.DISALLOW_RUN_IN_BACKGROUND, userId)) { @@ -869,7 +869,7 @@ class UserController implements Handler.Callback { * {@code userId}. The returned list includes {@code userId}. */ @GuardedBy("mLock") - private @NonNull int[] getUsersToStopLU(int userId) { + private @NonNull int[] getUsersToStopLU(@UserIdInt int userId) { int startedUsersSize = mStartedUsers.size(); IntArray userIds = new IntArray(); userIds.add(userId); @@ -892,7 +892,7 @@ class UserController implements Handler.Callback { return userIds.toArray(); } - private void forceStopUser(int userId, String reason) { + private void forceStopUser(@UserIdInt int userId, String reason) { mInjector.activityManagerForceStopPackage(userId, reason); Intent intent = new Intent(Intent.ACTION_USER_STOPPED); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY @@ -965,7 +965,7 @@ class UserController implements Handler.Callback { } } - boolean startUser(final int userId, final boolean foreground) { + boolean startUser(final @UserIdInt int userId, final boolean foreground) { return startUser(userId, foreground, null); } @@ -1002,7 +1002,7 @@ class UserController implements Handler.Callback { * @return true if the user has been successfully started */ boolean startUser( - final int userId, + final @UserIdInt int userId, final boolean foreground, @Nullable IProgressListener unlockListener) { @@ -1018,7 +1018,7 @@ class UserController implements Handler.Callback { } } - private boolean startUserInternal(int userId, boolean foreground, + private boolean startUserInternal(@UserIdInt int userId, boolean foreground, @Nullable IProgressListener unlockListener, @NonNull TimingsTraceAndSlog t) { Slog.i(TAG, "Starting userid:" + userId + " fg:" + foreground); @@ -1257,7 +1257,8 @@ class UserController implements Handler.Callback { } } - boolean unlockUser(final int userId, byte[] token, byte[] secret, IProgressListener listener) { + boolean unlockUser(final @UserIdInt int userId, byte[] token, byte[] secret, + IProgressListener listener) { checkCallingPermission(INTERACT_ACROSS_USERS_FULL, "unlockUser"); final long binderToken = Binder.clearCallingIdentity(); try { @@ -1273,12 +1274,12 @@ class UserController implements Handler.Callback { * when the credential-encrypted storage isn't tied to a user-provided * PIN or pattern. */ - private boolean maybeUnlockUser(final int userId) { + private boolean maybeUnlockUser(final @UserIdInt int userId) { // Try unlocking storage using empty token return unlockUserCleared(userId, null, null, null); } - private static void notifyFinished(int userId, IProgressListener listener) { + private static void notifyFinished(@UserIdInt int userId, IProgressListener listener) { if (listener == null) return; try { listener.onFinished(userId, null); @@ -1286,7 +1287,7 @@ class UserController implements Handler.Callback { } } - private boolean unlockUserCleared(final int userId, byte[] token, byte[] secret, + private boolean unlockUserCleared(final @UserIdInt int userId, byte[] token, byte[] secret, IProgressListener listener) { UserState uss; if (!StorageManager.isUserKeyUnlocked(userId)) { @@ -1384,7 +1385,7 @@ class UserController implements Handler.Callback { getSwitchingFromSystemUserMessage(), getSwitchingToSystemUserMessage()); } - private void dispatchForegroundProfileChanged(int userId) { + private void dispatchForegroundProfileChanged(@UserIdInt int userId) { final int observerCount = mUserSwitchObservers.beginBroadcast(); for (int i = 0; i < observerCount; i++) { try { @@ -1397,7 +1398,7 @@ class UserController implements Handler.Callback { } /** Called on handler thread */ - void dispatchUserSwitchComplete(int userId) { + void dispatchUserSwitchComplete(@UserIdInt int userId) { mInjector.getWindowManager().setSwitchingUser(false); final int observerCount = mUserSwitchObservers.beginBroadcast(); for (int i = 0; i < observerCount; i++) { @@ -1409,7 +1410,7 @@ class UserController implements Handler.Callback { mUserSwitchObservers.finishBroadcast(); } - private void dispatchLockedBootComplete(int userId) { + private void dispatchLockedBootComplete(@UserIdInt int userId) { final int observerCount = mUserSwitchObservers.beginBroadcast(); for (int i = 0; i < observerCount; i++) { try { @@ -1596,7 +1597,7 @@ class UserController implements Handler.Callback { } - int handleIncomingUser(int callingPid, int callingUid, int userId, boolean allowAll, + int handleIncomingUser(int callingPid, int callingUid, @UserIdInt int userId, boolean allowAll, int allowMode, String name, String callerPackage) { final int callingUserId = UserHandle.getUserId(callingUid); if (callingUserId == userId) { @@ -1682,12 +1683,12 @@ class UserController implements Handler.Callback { return targetUserId; } - int unsafeConvertIncomingUser(int userId) { + int unsafeConvertIncomingUser(@UserIdInt int userId) { return (userId == UserHandle.USER_CURRENT || userId == UserHandle.USER_CURRENT_OR_SELF) ? getCurrentUserId(): userId; } - void ensureNotSpecialUser(int userId) { + void ensureNotSpecialUser(@UserIdInt int userId) { if (userId >= 0) { return; } @@ -1700,7 +1701,7 @@ class UserController implements Handler.Callback { mUserSwitchObservers.register(observer, name); } - void sendForegroundProfileChanged(int userId) { + void sendForegroundProfileChanged(@UserIdInt int userId) { mHandler.removeMessages(FOREGROUND_PROFILE_CHANGED_MSG); mHandler.obtainMessage(FOREGROUND_PROFILE_CHANGED_MSG, userId, 0).sendToTarget(); } @@ -1709,13 +1710,13 @@ class UserController implements Handler.Callback { mUserSwitchObservers.unregister(observer); } - UserState getStartedUserState(int userId) { + UserState getStartedUserState(@UserIdInt int userId) { synchronized (mLock) { return mStartedUsers.get(userId); } } - boolean hasStartedUserState(int userId) { + boolean hasStartedUserState(@UserIdInt int userId) { synchronized (mLock) { return mStartedUsers.get(userId) != null; } @@ -1804,7 +1805,7 @@ class UserController implements Handler.Callback { } } - boolean isUserRunning(int userId, int flags) { + boolean isUserRunning(@UserIdInt int userId, int flags) { UserState state = getStartedUserState(userId); if (state == null) { return false; @@ -1920,7 +1921,7 @@ class UserController implements Handler.Callback { } @GuardedBy("mLock") - private boolean isCurrentUserLU(int userId) { + private boolean isCurrentUserLU(@UserIdInt int userId) { return userId == getCurrentOrTargetUserIdLU(); } @@ -1929,7 +1930,7 @@ class UserController implements Handler.Callback { return ums != null ? ums.getUserIds() : new int[] { 0 }; } - private UserInfo getUserInfo(int userId) { + private UserInfo getUserInfo(@UserIdInt int userId) { return mInjector.getUserManager().getUserInfo(userId); } @@ -1943,7 +1944,7 @@ class UserController implements Handler.Callback { * * It doesn't handle other special user IDs such as {@link UserHandle#USER_CURRENT}. */ - int[] expandUserId(int userId) { + int[] expandUserId(@UserIdInt int userId) { if (userId != UserHandle.USER_ALL) { return new int[] {userId}; } else { @@ -1951,7 +1952,7 @@ class UserController implements Handler.Callback { } } - boolean exists(int userId) { + boolean exists(@UserIdInt int userId) { return mInjector.getUserManager().exists(userId); } @@ -1967,16 +1968,16 @@ class UserController implements Handler.Callback { } } - private void enforceShellRestriction(String restriction, int userHandle) { + private void enforceShellRestriction(String restriction, @UserIdInt int userId) { if (Binder.getCallingUid() == SHELL_UID) { - if (userHandle < 0 || hasUserRestriction(restriction, userHandle)) { + if (userId < 0 || hasUserRestriction(restriction, userId)) { throw new SecurityException("Shell does not have permission to access user " - + userHandle); + + userId); } } } - boolean hasUserRestriction(String restriction, int userId) { + boolean hasUserRestriction(String restriction, @UserIdInt int userId) { return mInjector.getUserManager().hasUserRestriction(restriction, userId); } @@ -1994,7 +1995,7 @@ class UserController implements Handler.Callback { } } - boolean isUserOrItsParentRunning(int userId) { + boolean isUserOrItsParentRunning(@UserIdInt int userId) { synchronized (mLock) { if (isUserRunning(userId, 0)) { return true; @@ -2007,7 +2008,7 @@ class UserController implements Handler.Callback { } } - boolean isCurrentProfile(int userId) { + boolean isCurrentProfile(@UserIdInt int userId) { synchronized (mLock) { return ArrayUtils.contains(mCurrentProfileIds, userId); } @@ -2019,7 +2020,7 @@ class UserController implements Handler.Callback { } } - void onUserRemoved(int userId) { + void onUserRemoved(@UserIdInt int userId) { synchronized (mLock) { int size = mUserProfileGroupIds.size(); for (int i = size - 1; i >= 0; i--) { @@ -2037,7 +2038,7 @@ class UserController implements Handler.Callback { * Returns whether the given user requires credential entry at this time. This is used to * intercept activity launches for work apps when the Work Challenge is present. */ - protected boolean shouldConfirmCredentials(int userId) { + protected boolean shouldConfirmCredentials(@UserIdInt int userId) { synchronized (mLock) { if (mStartedUsers.get(userId) == null) { return false; @@ -2265,7 +2266,7 @@ class UserController implements Handler.Callback { IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions, boolean ordered, boolean sticky, int callingPid, int callingUid, int realCallingUid, - int realCallingPid, int userId) { + int realCallingPid, @UserIdInt int userId) { // TODO b/64165549 Verify that mLock is not held before calling AMS methods synchronized (mService) { return mService.broadcastIntentLocked(null, null, intent, resolvedType, resultTo, @@ -2282,11 +2283,11 @@ class UserController implements Handler.Callback { WindowManagerService getWindowManager() { return mService.mWindowManager; } - void activityManagerOnUserStopped(int userId) { + void activityManagerOnUserStopped(@UserIdInt int userId) { LocalServices.getService(ActivityTaskManagerInternal.class).onUserStopped(userId); } - void systemServiceManagerCleanupUser(int userId) { + void systemServiceManagerCleanupUser(@UserIdInt int userId) { mService.mSystemServiceManager.cleanupUser(userId); } @@ -2330,7 +2331,7 @@ class UserController implements Handler.Callback { } } - void sendPreBootBroadcast(int userId, boolean quiet, final Runnable onFinish) { + void sendPreBootBroadcast(@UserIdInt int userId, boolean quiet, final Runnable onFinish) { new PreBootBroadcaster(mService, userId, null, quiet) { @Override public void onFinished() { @@ -2339,7 +2340,7 @@ class UserController implements Handler.Callback { }.sendNext(); } - void activityManagerForceStopPackage(int userId, String reason) { + void activityManagerForceStopPackage(@UserIdInt int userId, String reason) { synchronized (mService) { mService.forceStopPackageLocked(null, -1, false, false, true, false, false, userId, reason); @@ -2351,11 +2352,11 @@ class UserController implements Handler.Callback { return mService.checkComponentPermission(permission, pid, uid, owningUid, exported); } - protected void startHomeActivity(int userId, String reason) { + protected void startHomeActivity(@UserIdInt int userId, String reason) { mService.mAtmInternal.startHomeActivity(userId, reason); } - void startUserWidgets(int userId) { + void startUserWidgets(@UserIdInt int userId) { AppWidgetManagerInternal awm = LocalServices.getService(AppWidgetManagerInternal.class); if (awm != null) { // Out of band, because this is called during a sequence with @@ -2370,13 +2371,13 @@ class UserController implements Handler.Callback { mService.mAtmInternal.updateUserConfiguration(); } - void clearBroadcastQueueForUser(int userId) { + void clearBroadcastQueueForUser(@UserIdInt int userId) { synchronized (mService) { mService.clearBroadcastQueueForUserLocked(userId); } } - void loadUserRecents(int userId) { + void loadUserRecents(@UserIdInt int userId) { mService.mAtmInternal.loadRecentTasksForUser(userId); } @@ -2384,7 +2385,7 @@ class UserController implements Handler.Callback { mService.startPersistentApps(matchFlags); } - void installEncryptionUnawareProviders(int userId) { + void installEncryptionUnawareProviders(@UserIdInt int userId) { mService.installEncryptionUnawareProviders(userId); } @@ -2417,11 +2418,11 @@ class UserController implements Handler.Callback { } } - void stackSupervisorRemoveUser(int userId) { + void stackSupervisorRemoveUser(@UserIdInt int userId) { mService.mAtmInternal.removeUser(userId); } - protected boolean stackSupervisorSwitchUser(int userId, UserState uss) { + protected boolean stackSupervisorSwitchUser(@UserIdInt int userId, UserState uss) { return mService.mAtmInternal.switchUser(userId, uss); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index ab77cc6de819..b124c4b18efa 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -113,6 +113,7 @@ import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo; import static com.android.server.pm.PackageManagerServiceUtils.verifySignatures; import android.Manifest; +import android.annotation.AppIdInt; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -12059,11 +12060,12 @@ public class PackageManagerService extends IPackageManager.Stub | IntentFilter.MATCH_ADJUSTMENT_NORMAL; } - private void killApplication(String pkgName, int appId, String reason) { + private void killApplication(String pkgName, @AppIdInt int appId, String reason) { killApplication(pkgName, appId, UserHandle.USER_ALL, reason); } - private void killApplication(String pkgName, int appId, int userId, String reason) { + private void killApplication(String pkgName, @AppIdInt int appId, + @UserIdInt int userId, String reason) { // Request the ActivityManager to kill the process(only for existing packages) // so that we do not end up in a confused state while the user is still using the older // version of the application while the new one gets installed. @@ -12438,7 +12440,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public void sendPackageAddedForNewUsers(String packageName, boolean sendBootCompleted, - boolean includeStopped, int appId, int[] userIds, int[] instantUserIds) { + boolean includeStopped, @AppIdInt int appId, int[] userIds, int[] instantUserIds) { if (ArrayUtils.isEmpty(userIds) && ArrayUtils.isEmpty(instantUserIds)) { return; } @@ -18903,7 +18905,8 @@ public class PackageManagerService extends IPackageManager.Stub * Remove entries from the keystore daemon. Will only remove it if the * {@code appId} is valid. */ - private static void removeKeystoreDataIfNeeded(UserManagerInternal um, int userId, int appId) { + private static void removeKeystoreDataIfNeeded(UserManagerInternal um, @UserIdInt int userId, + @AppIdInt int appId) { if (appId < 0) { return; } @@ -18989,7 +18992,7 @@ public class PackageManagerService extends IPackageManager.Stub } @Override - public void getPackageSizeInfo(final String packageName, int userHandle, + public void getPackageSizeInfo(final String packageName, int userId, final IPackageStatsObserver observer) { throw new UnsupportedOperationException( "Shame on you for calling the hidden API getPackageSizeInfo(). Shame!"); @@ -22494,24 +22497,24 @@ public class PackageManagerService extends IPackageManager.Stub } /** Called by UserManagerService */ - void cleanUpUser(UserManagerService userManager, int userHandle) { + void cleanUpUser(UserManagerService userManager, @UserIdInt int userId) { synchronized (mLock) { - mDirtyUsers.remove(userHandle); - mUserNeedsBadging.delete(userHandle); - mSettings.removeUserLPw(userHandle); - mPendingBroadcasts.remove(userHandle); - mInstantAppRegistry.onUserRemovedLPw(userHandle); - removeUnusedPackagesLPw(userManager, userHandle); + mDirtyUsers.remove(userId); + mUserNeedsBadging.delete(userId); + mSettings.removeUserLPw(userId); + mPendingBroadcasts.remove(userId); + mInstantAppRegistry.onUserRemovedLPw(userId); + removeUnusedPackagesLPw(userManager, userId); } } /** - * We're removing userHandle and would like to remove any downloaded packages + * We're removing userId and would like to remove any downloaded packages * that are no longer in use by any other user. - * @param userHandle the user being removed + * @param userId the user being removed */ @GuardedBy("mLock") - private void removeUnusedPackagesLPw(UserManagerService userManager, final int userHandle) { + private void removeUnusedPackagesLPw(UserManagerService userManager, final int userId) { final boolean DEBUG_CLEAN_APKS = false; int [] users = userManager.getUserIds(); Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator(); @@ -22535,7 +22538,7 @@ public class PackageManagerService extends IPackageManager.Stub } } else { for (int i = 0; i < users.length; i++) { - if (users[i] != userHandle && ps.getInstalled(users[i])) { + if (users[i] != userId && ps.getInstalled(users[i])) { keep = true; if (DEBUG_CLEAN_APKS) { Slog.i(TAG, " Keeping package " + packageName + " for user " @@ -22551,7 +22554,7 @@ public class PackageManagerService extends IPackageManager.Stub } //end run mHandler.post(() -> deletePackageX(packageName, PackageManager.VERSION_CODE_HIGHEST, - userHandle, 0)); + userId, 0)); } } } diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 65fb35d34c01..81723cbe22c5 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -404,10 +404,10 @@ public class UserManagerService extends IUserManager.Stub { return; } final IntentSender target = intent.getParcelableExtra(Intent.EXTRA_INTENT); - final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_ID, UserHandle.USER_NULL); + final int userId = intent.getIntExtra(Intent.EXTRA_USER_ID, UserHandle.USER_NULL); // Call setQuietModeEnabled on bg thread to avoid ANR BackgroundThread.getHandler().post(() -> - setQuietModeEnabled(userHandle, false, target, /* callingPackage */ null)); + setQuietModeEnabled(userId, false, target, /* callingPackage */ null)); } }; @@ -482,9 +482,9 @@ public class UserManagerService extends IUserManager.Stub { } @Override - public void onStartUser(int userHandle) { + public void onStartUser(@UserIdInt int userId) { synchronized (mUms.mUsersLock) { - final UserData user = mUms.getUserDataLU(userHandle); + final UserData user = mUms.getUserDataLU(userId); if (user != null) { user.startRealtime = SystemClock.elapsedRealtime(); } @@ -492,9 +492,9 @@ public class UserManagerService extends IUserManager.Stub { } @Override - public void onUnlockUser(int userHandle) { + public void onUnlockUser(@UserIdInt int userId) { synchronized (mUms.mUsersLock) { - final UserData user = mUms.getUserDataLU(userHandle); + final UserData user = mUms.getUserDataLU(userId); if (user != null) { user.unlockRealtime = SystemClock.elapsedRealtime(); } @@ -502,9 +502,9 @@ public class UserManagerService extends IUserManager.Stub { } @Override - public void onStopUser(int userHandle) { + public void onStopUser(@UserIdInt int userId) { synchronized (mUms.mUsersLock) { - final UserData user = mUms.getUserDataLU(userHandle); + final UserData user = mUms.getUserDataLU(userId); if (user != null) { user.startRealtime = 0; user.unlockRealtime = 0; @@ -610,7 +610,7 @@ public class UserManagerService extends IUserManager.Stub { } @Override - public String getUserAccount(int userId) { + public String getUserAccount(@UserIdInt int userId) { checkManageUserAndAcrossUsersFullPermission("get user account"); synchronized (mUsersLock) { return mUsers.get(userId).account; @@ -618,7 +618,7 @@ public class UserManagerService extends IUserManager.Stub { } @Override - public void setUserAccount(int userId, String accountName) { + public void setUserAccount(@UserIdInt int userId, String accountName) { checkManageUserAndAcrossUsersFullPermission("set user account"); UserData userToUpdate = null; synchronized (mPackagesLock) { @@ -676,7 +676,7 @@ public class UserManagerService extends IUserManager.Stub { } @Override - public List<UserInfo> getProfiles(int userId, boolean enabledOnly) { + public List<UserInfo> getProfiles(@UserIdInt int userId, boolean enabledOnly) { boolean returnFullInfo = true; if (userId != UserHandle.getCallingUserId()) { checkManageOrCreateUsersPermission("getting profiles related to user " + userId); @@ -694,7 +694,7 @@ public class UserManagerService extends IUserManager.Stub { } @Override - public int[] getProfileIds(int userId, boolean enabledOnly) { + public int[] getProfileIds(@UserIdInt int userId, boolean enabledOnly) { if (userId != UserHandle.getCallingUserId()) { checkManageOrCreateUsersPermission("getting profiles related to user " + userId); } @@ -710,7 +710,8 @@ public class UserManagerService extends IUserManager.Stub { /** Assume permissions already checked and caller's identity cleared */ @GuardedBy("mUsersLock") - private List<UserInfo> getProfilesLU(int userId, boolean enabledOnly, boolean fullInfo) { + private List<UserInfo> getProfilesLU(@UserIdInt int userId, boolean enabledOnly, + boolean fullInfo) { IntArray profileIds = getProfileIdsLU(userId, enabledOnly); ArrayList<UserInfo> users = new ArrayList<>(profileIds.size()); for (int i = 0; i < profileIds.size(); i++) { @@ -733,7 +734,7 @@ public class UserManagerService extends IUserManager.Stub { * Assume permissions already checked and caller's identity cleared */ @GuardedBy("mUsersLock") - private IntArray getProfileIdsLU(int userId, boolean enabledOnly) { + private IntArray getProfileIdsLU(@UserIdInt int userId, boolean enabledOnly) { UserInfo user = getUserInfoLU(userId); IntArray result = new IntArray(mUsers.size()); if (user == null) { @@ -761,28 +762,28 @@ public class UserManagerService extends IUserManager.Stub { } @Override - public int getCredentialOwnerProfile(int userHandle) { + public int getCredentialOwnerProfile(@UserIdInt int userId) { checkManageUsersPermission("get the credential owner"); - if (!mLockPatternUtils.isSeparateProfileChallengeEnabled(userHandle)) { + if (!mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)) { synchronized (mUsersLock) { - UserInfo profileParent = getProfileParentLU(userHandle); + UserInfo profileParent = getProfileParentLU(userId); if (profileParent != null) { return profileParent.id; } } } - return userHandle; + return userId; } @Override - public boolean isSameProfileGroup(int userId, int otherUserId) { + public boolean isSameProfileGroup(@UserIdInt int userId, int otherUserId) { if (userId == otherUserId) return true; checkManageUsersPermission("check if in the same profile group"); return isSameProfileGroupNoChecks(userId, otherUserId); } - private boolean isSameProfileGroupNoChecks(int userId, int otherUserId) { + private boolean isSameProfileGroupNoChecks(@UserIdInt int userId, int otherUserId) { synchronized (mUsersLock) { UserInfo userInfo = getUserInfoLU(userId); if (userInfo == null || userInfo.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID) { @@ -798,27 +799,27 @@ public class UserManagerService extends IUserManager.Stub { } @Override - public UserInfo getProfileParent(int userHandle) { + public UserInfo getProfileParent(@UserIdInt int userId) { checkManageUsersPermission("get the profile parent"); synchronized (mUsersLock) { - return getProfileParentLU(userHandle); + return getProfileParentLU(userId); } } @Override - public int getProfileParentId(int userHandle) { + public int getProfileParentId(@UserIdInt int userId) { checkManageUsersPermission("get the profile parent"); - return mLocalService.getProfileParentId(userHandle); + return mLocalService.getProfileParentId(userId); } @GuardedBy("mUsersLock") - private UserInfo getProfileParentLU(int userHandle) { - UserInfo profile = getUserInfoLU(userHandle); + private UserInfo getProfileParentLU(@UserIdInt int userId) { + UserInfo profile = getUserInfoLU(userId); if (profile == null) { return null; } int parentUserId = profile.profileGroupId; - if (parentUserId == userHandle || parentUserId == UserInfo.NO_PROFILE_GROUP_ID) { + if (parentUserId == userId || parentUserId == UserInfo.NO_PROFILE_GROUP_ID) { return null; } else { return getUserInfoLU(parentUserId); @@ -848,7 +849,7 @@ public class UserManagerService extends IUserManager.Stub { @Override public boolean requestQuietModeEnabled(@NonNull String callingPackage, boolean enableQuietMode, - int userHandle, @Nullable IntentSender target) { + @UserIdInt int userId, @Nullable IntentSender target) { Preconditions.checkNotNull(callingPackage); if (enableQuietMode && target != null) { @@ -862,17 +863,17 @@ public class UserManagerService extends IUserManager.Stub { boolean result = false; if (enableQuietMode) { setQuietModeEnabled( - userHandle, true /* enableQuietMode */, target, callingPackage); + userId, true /* enableQuietMode */, target, callingPackage); result = true; } else { boolean needToShowConfirmCredential = - mLockPatternUtils.isSecure(userHandle) - && !StorageManager.isUserKeyUnlocked(userHandle); + mLockPatternUtils.isSecure(userId) + && !StorageManager.isUserKeyUnlocked(userId); if (needToShowConfirmCredential) { - showConfirmCredentialToDisableQuietMode(userHandle, target); + showConfirmCredentialToDisableQuietMode(userId, target); } else { setQuietModeEnabled( - userHandle, false /* enableQuietMode */, target, callingPackage); + userId, false /* enableQuietMode */, target, callingPackage); result = true; } } @@ -922,16 +923,16 @@ public class UserManagerService extends IUserManager.Stub { + "default launcher nor has MANAGE_USERS/MODIFY_QUIET_MODE permission"); } - private void setQuietModeEnabled(int userHandle, boolean enableQuietMode, + private void setQuietModeEnabled(@UserIdInt int userId, boolean enableQuietMode, IntentSender target, @Nullable String callingPackage) { final UserInfo profile, parent; final UserData profileUserData; synchronized (mUsersLock) { - profile = getUserInfoLU(userHandle); - parent = getProfileParentLU(userHandle); + profile = getUserInfoLU(userId); + parent = getProfileParentLU(userId); if (profile == null || !profile.isManagedProfile()) { - throw new IllegalArgumentException("User " + userHandle + " is not a profile"); + throw new IllegalArgumentException("User " + userId + " is not a profile"); } if (profile.isQuietModeEnabled() == enableQuietMode) { Slog.i(LOG_TAG, "Quiet mode is already " + enableQuietMode); @@ -945,17 +946,17 @@ public class UserManagerService extends IUserManager.Stub { } try { if (enableQuietMode) { - ActivityManager.getService().stopUser(userHandle, /* force */true, null); + ActivityManager.getService().stopUser(userId, /* force */true, null); LocalServices.getService(ActivityManagerInternal.class) - .killForegroundAppsForUser(userHandle); + .killForegroundAppsForUser(userId); } else { IProgressListener callback = target != null ? new DisableQuietModeUserUnlockedCallback(target) : null; ActivityManager.getService().startUserInBackgroundWithListener( - userHandle, callback); + userId, callback); } - logQuietModeEnabled(userHandle, enableQuietMode, callingPackage); + logQuietModeEnabled(userId, enableQuietMode, callingPackage); } catch (RemoteException e) { // Should not happen, same process. e.rethrowAsRuntimeException(); @@ -964,11 +965,11 @@ public class UserManagerService extends IUserManager.Stub { enableQuietMode); } - private void logQuietModeEnabled(int userHandle, boolean enableQuietMode, + private void logQuietModeEnabled(@UserIdInt int userId, boolean enableQuietMode, @Nullable String callingPackage) { UserData userData; synchronized (mUsersLock) { - userData = getUserDataLU(userHandle); + userData = getUserDataLU(userId); } if (userData == null) { return; @@ -987,11 +988,11 @@ public class UserManagerService extends IUserManager.Stub { } @Override - public boolean isQuietModeEnabled(int userHandle) { + public boolean isQuietModeEnabled(@UserIdInt int userId) { synchronized (mPackagesLock) { UserInfo info; synchronized (mUsersLock) { - info = getUserInfoLU(userHandle); + info = getUserInfoLU(userId); } if (info == null || !info.isManagedProfile()) { return false; @@ -1004,15 +1005,14 @@ public class UserManagerService extends IUserManager.Stub { * Show confirm credential screen to unlock user in order to turn off quiet mode. */ private void showConfirmCredentialToDisableQuietMode( - @UserIdInt int userHandle, @Nullable IntentSender target) { + @UserIdInt int userId, @Nullable IntentSender target) { // otherwise, we show a profile challenge to trigger decryption of the user final KeyguardManager km = (KeyguardManager) mContext.getSystemService( Context.KEYGUARD_SERVICE); - // We should use userHandle not credentialOwnerUserId here, as even if it is unified + // We should use userId not credentialOwnerUserId here, as even if it is unified // lock, confirm screenlock page will know and show personal challenge, and unlock // work profile when personal challenge is correct - final Intent unlockIntent = km.createConfirmDeviceCredentialIntent(null, null, - userHandle); + final Intent unlockIntent = km.createConfirmDeviceCredentialIntent(null, null, userId); if (unlockIntent == null) { return; } @@ -1021,7 +1021,7 @@ public class UserManagerService extends IUserManager.Stub { if (target != null) { callBackIntent.putExtra(Intent.EXTRA_INTENT, target); } - callBackIntent.putExtra(Intent.EXTRA_USER_ID, userHandle); + callBackIntent.putExtra(Intent.EXTRA_USER_ID, userId); callBackIntent.setPackage(mContext.getPackageName()); callBackIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); final PendingIntent pendingIntent = PendingIntent.getBroadcast( @@ -1039,7 +1039,7 @@ public class UserManagerService extends IUserManager.Stub { } @Override - public void setUserEnabled(int userId) { + public void setUserEnabled(@UserIdInt int userId) { checkManageUsersPermission("enable user"); synchronized (mPackagesLock) { UserInfo info; @@ -1054,7 +1054,7 @@ public class UserManagerService extends IUserManager.Stub { } @Override - public void setUserAdmin(int userId) { + public void setUserAdmin(@UserIdInt int userId) { checkManageUserAndAcrossUsersFullPermission("set user admin"); synchronized (mPackagesLock) { @@ -1097,7 +1097,7 @@ public class UserManagerService extends IUserManager.Stub { } @Override - public UserInfo getUserInfo(int userId) { + public UserInfo getUserInfo(@UserIdInt int userId) { checkManageOrCreateUsersPermission("query user"); synchronized (mUsersLock) { return userWithName(getUserInfoLU(userId)); @@ -1128,7 +1128,7 @@ public class UserManagerService extends IUserManager.Stub { } @Override - public boolean isManagedProfile(int userId) { + public boolean isManagedProfile(@UserIdInt int userId) { checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "isManagedProfile"); synchronized (mUsersLock) { UserInfo userInfo = getUserInfoLU(userId); @@ -1137,19 +1137,19 @@ public class UserManagerService extends IUserManager.Stub { } @Override - public boolean isUserUnlockingOrUnlocked(int userId) { + public boolean isUserUnlockingOrUnlocked(@UserIdInt int userId) { checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "isUserUnlockingOrUnlocked"); return mLocalService.isUserUnlockingOrUnlocked(userId); } @Override - public boolean isUserUnlocked(int userId) { + public boolean isUserUnlocked(@UserIdInt int userId) { checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "isUserUnlocked"); return mLocalService.isUserUnlocked(userId); } @Override - public boolean isUserRunning(int userId) { + public boolean isUserRunning(@UserIdInt int userId) { checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "isUserRunning"); return mLocalService.isUserRunning(userId); } @@ -1190,7 +1190,8 @@ public class UserManagerService extends IUserManager.Stub { } } - private void checkManageOrInteractPermIfCallerInOtherProfileGroup(int userId, String name) { + private void checkManageOrInteractPermIfCallerInOtherProfileGroup(@UserIdInt int userId, + String name) { int callingUserId = UserHandle.getCallingUserId(); if (callingUserId == userId || isSameProfileGroupNoChecks(callingUserId, userId) || hasManageUsersPermission()) { @@ -1204,7 +1205,7 @@ public class UserManagerService extends IUserManager.Stub { } @Override - public boolean isDemoUser(int userId) { + public boolean isDemoUser(@UserIdInt int userId) { int callingUserId = UserHandle.getCallingUserId(); if (callingUserId != userId && !hasManageUsersPermission()) { throw new SecurityException("You need MANAGE_USERS permission to query if u=" + userId @@ -1224,7 +1225,7 @@ public class UserManagerService extends IUserManager.Stub { } @Override - public boolean canHaveRestrictedProfile(int userId) { + public boolean canHaveRestrictedProfile(@UserIdInt int userId) { checkManageUsersPermission("canHaveRestrictedProfile"); synchronized (mUsersLock) { final UserInfo userInfo = getUserInfoLU(userId); @@ -1260,7 +1261,7 @@ public class UserManagerService extends IUserManager.Stub { * Should be locked on mUsers before calling this. */ @GuardedBy("mUsersLock") - private UserInfo getUserInfoLU(int userId) { + private UserInfo getUserInfoLU(@UserIdInt int userId) { final UserData userData = mUsers.get(userId); // If it is partial and not in the process of being removed, return as unknown user. if (userData != null && userData.info.partial && !mRemovingUserIds.get(userId)) { @@ -1271,7 +1272,7 @@ public class UserManagerService extends IUserManager.Stub { } @GuardedBy("mUsersLock") - private UserData getUserDataLU(int userId) { + private UserData getUserDataLU(@UserIdInt int userId) { final UserData userData = mUsers.get(userId); // If it is partial and not in the process of being removed, return as unknown user. if (userData != null && userData.info.partial && !mRemovingUserIds.get(userId)) { @@ -1284,7 +1285,7 @@ public class UserManagerService extends IUserManager.Stub { * Obtains {@link #mUsersLock} and return UserInfo from mUsers. * <p>No permissions checking or any addition checks are made</p> */ - private UserInfo getUserInfoNoChecks(int userId) { + private UserInfo getUserInfoNoChecks(@UserIdInt int userId) { synchronized (mUsersLock) { final UserData userData = mUsers.get(userId); return userData != null ? userData.info : null; @@ -1295,19 +1296,19 @@ public class UserManagerService extends IUserManager.Stub { * Obtains {@link #mUsersLock} and return UserData from mUsers. * <p>No permissions checking or any addition checks are made</p> */ - private UserData getUserDataNoChecks(int userId) { + private UserData getUserDataNoChecks(@UserIdInt int userId) { synchronized (mUsersLock) { return mUsers.get(userId); } } /** Called by PackageManagerService */ - public boolean exists(int userId) { + public boolean exists(@UserIdInt int userId) { return mLocalService.exists(userId); } @Override - public void setUserName(int userId, String name) { + public void setUserName(@UserIdInt int userId, String name) { checkManageUsersPermission("rename users"); boolean changed = false; synchronized (mPackagesLock) { @@ -1333,7 +1334,7 @@ public class UserManagerService extends IUserManager.Stub { } @Override - public void setUserIcon(int userId, Bitmap bitmap) { + public void setUserIcon(@UserIdInt int userId, Bitmap bitmap) { checkManageUsersPermission("update users"); if (hasUserRestriction(UserManager.DISALLOW_SET_USER_ICON, userId)) { Log.w(LOG_TAG, "Cannot set user icon. DISALLOW_SET_USER_ICON is enabled."); @@ -1344,7 +1345,7 @@ public class UserManagerService extends IUserManager.Stub { - private void sendUserInfoChangedBroadcast(int userId) { + private void sendUserInfoChangedBroadcast(@UserIdInt int userId) { Intent changedIntent = new Intent(Intent.ACTION_USER_INFO_CHANGED); changedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId); changedIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); @@ -1389,7 +1390,7 @@ public class UserManagerService extends IUserManager.Stub { return null; } - public void makeInitialized(int userId) { + public void makeInitialized(@UserIdInt int userId) { checkManageUsersPermission("makeInitialized"); boolean scheduleWriteUser = false; UserData userData; @@ -1447,8 +1448,8 @@ public class UserManagerService extends IUserManager.Stub { /** * See {@link UserManagerInternal#setDevicePolicyUserRestrictions} */ - private void setDevicePolicyUserRestrictionsInner(int userId, @Nullable Bundle restrictions, - boolean isDeviceOwner, int cameraRestrictionScope) { + private void setDevicePolicyUserRestrictionsInner(@UserIdInt int userId, + @Nullable Bundle restrictions, boolean isDeviceOwner, int cameraRestrictionScope) { final Bundle global = new Bundle(); final Bundle local = new Bundle(); @@ -1505,8 +1506,8 @@ public class UserManagerService extends IUserManager.Stub { * empty, record is removed from the array. * @return whether restrictions bundle is different from the old one. */ - private boolean updateRestrictionsIfNeededLR(int userId, @Nullable Bundle restrictions, - SparseArray<Bundle> restrictionsArray) { + private boolean updateRestrictionsIfNeededLR(@UserIdInt int userId, + @Nullable Bundle restrictions, SparseArray<Bundle> restrictionsArray) { final boolean changed = !UserRestrictionsUtils.areEqual(restrictionsArray.get(userId), restrictions); if (changed) { @@ -1520,7 +1521,7 @@ public class UserManagerService extends IUserManager.Stub { } @GuardedBy("mRestrictionsLock") - private Bundle computeEffectiveUserRestrictionsLR(int userId) { + private Bundle computeEffectiveUserRestrictionsLR(@UserIdInt int userId) { final Bundle baseRestrictions = UserRestrictionsUtils.nonNull(mBaseUserRestrictions.get(userId)); final Bundle global = UserRestrictionsUtils.mergeAll(mDevicePolicyGlobalUserRestrictions); @@ -1538,14 +1539,14 @@ public class UserManagerService extends IUserManager.Stub { } @GuardedBy("mRestrictionsLock") - private void invalidateEffectiveUserRestrictionsLR(int userId) { + private void invalidateEffectiveUserRestrictionsLR(@UserIdInt int userId) { if (DBG) { Log.d(LOG_TAG, "invalidateEffectiveUserRestrictions userId=" + userId); } mCachedEffectiveUserRestrictions.remove(userId); } - private Bundle getEffectiveUserRestrictions(int userId) { + private Bundle getEffectiveUserRestrictions(@UserIdInt int userId) { synchronized (mRestrictionsLock) { Bundle restrictions = mCachedEffectiveUserRestrictions.get(userId); if (restrictions == null) { @@ -1558,7 +1559,7 @@ public class UserManagerService extends IUserManager.Stub { /** @return a specific user restriction that's in effect currently. */ @Override - public boolean hasUserRestriction(String restrictionKey, int userId) { + public boolean hasUserRestriction(String restrictionKey, @UserIdInt int userId) { return mLocalService.hasUserRestriction(restrictionKey, userId); } @@ -1593,7 +1594,7 @@ public class UserManagerService extends IUserManager.Stub { * and {@link UserManager#RESTRICTION_SOURCE_PROFILE_OWNER} */ @Override - public int getUserRestrictionSource(String restrictionKey, int userId) { + public int getUserRestrictionSource(String restrictionKey, @UserIdInt int userId) { List<EnforcingUser> enforcingUsers = getUserRestrictionSources(restrictionKey, userId); // Get "bitwise or" of restriction sources for all enforcing users. int result = UserManager.RESTRICTION_NOT_SET; @@ -1652,12 +1653,12 @@ public class UserManagerService extends IUserManager.Stub { * {@link Bundle}. */ @Override - public Bundle getUserRestrictions(int userId) { + public Bundle getUserRestrictions(@UserIdInt int userId) { return UserRestrictionsUtils.clone(getEffectiveUserRestrictions(userId)); } @Override - public boolean hasBaseUserRestriction(String restrictionKey, int userId) { + public boolean hasBaseUserRestriction(String restrictionKey, @UserIdInt int userId) { checkManageUsersPermission("hasBaseUserRestriction"); if (!UserRestrictionsUtils.isValidRestriction(restrictionKey)) { return false; @@ -1669,7 +1670,7 @@ public class UserManagerService extends IUserManager.Stub { } @Override - public void setUserRestriction(String key, boolean value, int userId) { + public void setUserRestriction(String key, boolean value, @UserIdInt int userId) { checkManageUsersPermission("setUserRestriction"); if (!UserRestrictionsUtils.isValidRestriction(key)) { return; @@ -1695,7 +1696,7 @@ public class UserManagerService extends IUserManager.Stub { */ @GuardedBy("mRestrictionsLock") private void updateUserRestrictionsInternalLR( - @Nullable Bundle newBaseRestrictions, int userId) { + @Nullable Bundle newBaseRestrictions, @UserIdInt int userId) { final Bundle prevAppliedRestrictions = UserRestrictionsUtils.nonNull( mAppliedUserRestrictions.get(userId)); @@ -1779,7 +1780,7 @@ public class UserManagerService extends IUserManager.Stub { // Package private for the inner class. @GuardedBy("mRestrictionsLock") - void applyUserRestrictionsLR(int userId) { + void applyUserRestrictionsLR(@UserIdInt int userId) { updateUserRestrictionsInternalLR(null, userId); } @@ -1831,7 +1832,7 @@ public class UserManagerService extends IUserManager.Stub { } @Override - public boolean canAddMoreManagedProfiles(int userId, boolean allowedToRemoveOne) { + public boolean canAddMoreManagedProfiles(@UserIdInt int userId, boolean allowedToRemoveOne) { checkManageUsersPermission("check if more managed profiles can be added."); if (ActivityManager.isLowRamDeviceStatic()) { return false; @@ -2661,7 +2662,7 @@ public class UserManagerService extends IUserManager.Stub { /** * Removes the app restrictions file for a specific package and user id, if it exists. */ - private static void cleanAppRestrictionsForPackageLAr(String pkg, int userId) { + private static void cleanAppRestrictionsForPackageLAr(String pkg, @UserIdInt int userId) { File dir = Environment.getUserSystemDirectory(userId); File resFile = new File(dir, packageToRestrictionsFileName(pkg)); if (resFile.exists()) { @@ -2670,23 +2671,23 @@ public class UserManagerService extends IUserManager.Stub { } @Override - public UserInfo createProfileForUser(String name, int flags, int userId, + public UserInfo createProfileForUser(String name, int flags, @UserIdInt int userId, String[] disallowedPackages) { checkManageOrCreateUsersPermission(flags); return createUserInternal(name, flags, userId, disallowedPackages); } @Override - public UserInfo createProfileForUserEvenWhenDisallowed(String name, int flags, int userId, - String[] disallowedPackages) { + public UserInfo createProfileForUserEvenWhenDisallowed(String name, int flags, + @UserIdInt int userId, String[] disallowedPackages) { checkManageOrCreateUsersPermission(flags); return createUserInternalUnchecked(name, flags, userId, disallowedPackages); } @Override - public boolean removeUserEvenWhenDisallowed(@UserIdInt int userHandle) { + public boolean removeUserEvenWhenDisallowed(@UserIdInt int userId) { checkManageOrCreateUsersPermission("Only the system can remove users"); - return removeUserUnchecked(userHandle); + return removeUserUnchecked(userId); } @Override @@ -2871,7 +2872,7 @@ public class UserManagerService extends IUserManager.Stub { } @VisibleForTesting - void removeUserInfo(int userId) { + void removeUserInfo(@UserIdInt int userId) { synchronized (mUsers) { mUsers.remove(userId); } @@ -2923,11 +2924,11 @@ public class UserManagerService extends IUserManager.Stub { /** * Mark this guest user for deletion to allow us to create another guest * and switch to that user before actually removing this guest. - * @param userHandle the userid of the current guest + * @param userId the userid of the current guest * @return whether the user could be marked for deletion */ @Override - public boolean markGuestForDeletion(int userHandle) { + public boolean markGuestForDeletion(@UserIdInt int userId) { checkManageUsersPermission("Only the system can remove users"); if (getUserRestrictions(UserHandle.getCallingUserId()).getBoolean( UserManager.DISALLOW_REMOVE_USER, false)) { @@ -2940,8 +2941,8 @@ public class UserManagerService extends IUserManager.Stub { final UserData userData; synchronized (mPackagesLock) { synchronized (mUsersLock) { - userData = mUsers.get(userHandle); - if (userHandle == 0 || userData == null || mRemovingUserIds.get(userHandle)) { + userData = mUsers.get(userId); + if (userId == 0 || userData == null || mRemovingUserIds.get(userId)) { return false; } } @@ -2968,16 +2969,16 @@ public class UserManagerService extends IUserManager.Stub { /** * Removes a user and all data directories created for that user. This method should be called * after the user's processes have been terminated. - * @param userHandle the user's id + * @param userId the user's id */ @Override - public boolean removeUser(int userHandle) { - Slog.i(LOG_TAG, "removeUser u" + userHandle); + public boolean removeUser(@UserIdInt int userId) { + Slog.i(LOG_TAG, "removeUser u" + userId); checkManageOrCreateUsersPermission("Only the system can remove users"); final boolean isManagedProfile; synchronized (mUsersLock) { - UserInfo userInfo = getUserInfoLU(userHandle); + UserInfo userInfo = getUserInfoLU(userId); isManagedProfile = userInfo != null && userInfo.isManagedProfile(); } String restriction = isManagedProfile @@ -2986,39 +2987,39 @@ public class UserManagerService extends IUserManager.Stub { Log.w(LOG_TAG, "Cannot remove user. " + restriction + " is enabled."); return false; } - return removeUserUnchecked(userHandle); + return removeUserUnchecked(userId); } - private boolean removeUserUnchecked(int userHandle) { + private boolean removeUserUnchecked(@UserIdInt int userId) { long ident = Binder.clearCallingIdentity(); try { final UserData userData; int currentUser = ActivityManager.getCurrentUser(); - if (currentUser == userHandle) { + if (currentUser == userId) { Log.w(LOG_TAG, "Current user cannot be removed."); return false; } synchronized (mPackagesLock) { synchronized (mUsersLock) { - userData = mUsers.get(userHandle); - if (userHandle == UserHandle.USER_SYSTEM) { + userData = mUsers.get(userId); + if (userId == UserHandle.USER_SYSTEM) { Log.e(LOG_TAG, "System user cannot be removed."); return false; } if (userData == null) { Log.e(LOG_TAG, String.format( - "Cannot remove user %d, invalid user id provided.", userHandle)); + "Cannot remove user %d, invalid user id provided.", userId)); return false; } - if (mRemovingUserIds.get(userHandle)) { + if (mRemovingUserIds.get(userId)) { Log.e(LOG_TAG, String.format( - "User %d is already scheduled for removal.", userHandle)); + "User %d is already scheduled for removal.", userId)); return false; } - addRemovingUserIdLocked(userHandle); + addRemovingUserIdLocked(userId); } // Set this to a partially created user, so that the user will be purged @@ -3031,7 +3032,7 @@ public class UserManagerService extends IUserManager.Stub { writeUserLP(userData); } try { - mAppOpsService.removeUser(userHandle); + mAppOpsService.removeUser(userId); } catch (RemoteException e) { Log.w(LOG_TAG, "Unable to notify AppOpsService of removing user.", e); } @@ -3043,17 +3044,17 @@ public class UserManagerService extends IUserManager.Stub { sendProfileRemovedBroadcast(userData.info.profileGroupId, userData.info.id); } - if (DBG) Slog.i(LOG_TAG, "Stopping user " + userHandle); + if (DBG) Slog.i(LOG_TAG, "Stopping user " + userId); int res; try { - res = ActivityManager.getService().stopUser(userHandle, /* force= */ true, + res = ActivityManager.getService().stopUser(userId, /* force= */ true, new IStopUserCallback.Stub() { @Override - public void userStopped(int userId) { - finishRemoveUser(userId); + public void userStopped(int userIdParam) { + finishRemoveUser(userIdParam); } @Override - public void userStopAborted(int userId) { + public void userStopAborted(int userIdParam) { } }); } catch (RemoteException e) { @@ -3068,7 +3069,7 @@ public class UserManagerService extends IUserManager.Stub { @GuardedBy("mUsersLock") @VisibleForTesting - void addRemovingUserIdLocked(int userId) { + void addRemovingUserIdLocked(@UserIdInt int userId) { // We remember deleted user IDs to prevent them from being // reused during the current boot; they can still be reused // after a reboot or recycling of userIds. @@ -3080,14 +3081,14 @@ public class UserManagerService extends IUserManager.Stub { } } - void finishRemoveUser(final int userHandle) { - if (DBG) Slog.i(LOG_TAG, "finishRemoveUser " + userHandle); + void finishRemoveUser(final @UserIdInt int userId) { + if (DBG) Slog.i(LOG_TAG, "finishRemoveUser " + userId); // Let other services shutdown any activity and clean up their state before completely // wiping the user's system directory and removing from the user list long ident = Binder.clearCallingIdentity(); try { Intent addedIntent = new Intent(Intent.ACTION_USER_REMOVED); - addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userHandle); + addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId); mContext.sendOrderedBroadcastAsUser(addedIntent, UserHandle.ALL, android.Manifest.permission.MANAGE_USERS, @@ -3097,15 +3098,15 @@ public class UserManagerService extends IUserManager.Stub { if (DBG) { Slog.i(LOG_TAG, "USER_REMOVED broadcast sent, cleaning up user data " - + userHandle); + + userId); } new Thread() { @Override public void run() { // Clean up any ActivityTaskManager state LocalServices.getService(ActivityTaskManagerInternal.class) - .onUserStopped(userHandle); - removeUserState(userHandle); + .onUserStopped(userId); + removeUserState(userId); } }.start(); } @@ -3117,47 +3118,46 @@ public class UserManagerService extends IUserManager.Stub { } } - private void removeUserState(final int userHandle) { + private void removeUserState(final @UserIdInt int userId) { try { - mContext.getSystemService(StorageManager.class).destroyUserKey(userHandle); + mContext.getSystemService(StorageManager.class).destroyUserKey(userId); } catch (IllegalStateException e) { // This may be simply because the user was partially created. - Slog.i(LOG_TAG, - "Destroying key for user " + userHandle + " failed, continuing anyway", e); + Slog.i(LOG_TAG, "Destroying key for user " + userId + " failed, continuing anyway", e); } // Cleanup gatekeeper secure user id try { final IGateKeeperService gk = GateKeeper.getService(); if (gk != null) { - gk.clearSecureUserId(userHandle); + gk.clearSecureUserId(userId); } } catch (Exception ex) { Slog.w(LOG_TAG, "unable to clear GK secure user id"); } // Cleanup package manager settings - mPm.cleanUpUser(this, userHandle); + mPm.cleanUpUser(this, userId); // Clean up all data before removing metadata - mUserDataPreparer.destroyUserData(userHandle, + mUserDataPreparer.destroyUserData(userId, StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE); // Remove this user from the list synchronized (mUsersLock) { - mUsers.remove(userHandle); - mIsUserManaged.delete(userHandle); + mUsers.remove(userId); + mIsUserManaged.delete(userId); } synchronized (mUserStates) { - mUserStates.delete(userHandle); + mUserStates.delete(userId); } synchronized (mRestrictionsLock) { - mBaseUserRestrictions.remove(userHandle); - mAppliedUserRestrictions.remove(userHandle); - mCachedEffectiveUserRestrictions.remove(userHandle); - mDevicePolicyLocalUserRestrictions.remove(userHandle); - if (mDevicePolicyGlobalUserRestrictions.get(userHandle) != null) { - mDevicePolicyGlobalUserRestrictions.remove(userHandle); + mBaseUserRestrictions.remove(userId); + mAppliedUserRestrictions.remove(userId); + mCachedEffectiveUserRestrictions.remove(userId); + mDevicePolicyLocalUserRestrictions.remove(userId); + if (mDevicePolicyGlobalUserRestrictions.get(userId) != null) { + mDevicePolicyGlobalUserRestrictions.remove(userId); applyUserRestrictionsForAllUsersLR(); } } @@ -3166,12 +3166,12 @@ public class UserManagerService extends IUserManager.Stub { writeUserListLP(); } // Remove user file - AtomicFile userFile = new AtomicFile(new File(mUsersDir, userHandle + XML_SUFFIX)); + AtomicFile userFile = new AtomicFile(new File(mUsersDir, userId + XML_SUFFIX)); userFile.delete(); updateUserIds(); if (RELEASE_DELETED_USER_ID) { synchronized (mUsers) { - mRemovingUserIds.delete(userHandle); + mRemovingUserIds.delete(userId); } } } @@ -3191,7 +3191,7 @@ public class UserManagerService extends IUserManager.Stub { } @Override - public Bundle getApplicationRestrictionsForUser(String packageName, int userId) { + public Bundle getApplicationRestrictionsForUser(String packageName, @UserIdInt int userId) { if (UserHandle.getCallingUserId() != userId || !UserHandle.isSameApp(Binder.getCallingUid(), getUidForPackage(packageName))) { checkSystemOrRoot("get application restrictions for other user/app " + packageName); @@ -3204,7 +3204,7 @@ public class UserManagerService extends IUserManager.Stub { @Override public void setApplicationRestrictions(String packageName, Bundle restrictions, - int userId) { + @UserIdInt int userId) { checkSystemOrRoot("set application restrictions"); if (restrictions != null) { restrictions.setDefusable(true); @@ -3238,7 +3238,8 @@ public class UserManagerService extends IUserManager.Stub { } @GuardedBy("mAppRestrictionsLock") - private static Bundle readApplicationRestrictionsLAr(String packageName, int userId) { + private static Bundle readApplicationRestrictionsLAr(String packageName, + @UserIdInt int userId) { AtomicFile restrictionsFile = new AtomicFile(new File(Environment.getUserSystemDirectory(userId), packageToRestrictionsFileName(packageName))); @@ -3332,7 +3333,7 @@ public class UserManagerService extends IUserManager.Stub { @GuardedBy("mAppRestrictionsLock") private static void writeApplicationRestrictionsLAr(String packageName, - Bundle restrictions, int userId) { + Bundle restrictions, @UserIdInt int userId) { AtomicFile restrictionsFile = new AtomicFile( new File(Environment.getUserSystemDirectory(userId), packageToRestrictionsFileName(packageName))); @@ -3410,17 +3411,17 @@ public class UserManagerService extends IUserManager.Stub { } @Override - public int getUserSerialNumber(int userHandle) { + public int getUserSerialNumber(@UserIdInt int userId) { synchronized (mUsersLock) { - final UserInfo userInfo = getUserInfoLU(userHandle); + final UserInfo userInfo = getUserInfoLU(userId); return userInfo != null ? userInfo.serialNumber : -1; } } @Override - public boolean isUserNameSet(int userHandle) { + public boolean isUserNameSet(@UserIdInt int userId) { synchronized (mUsersLock) { - final UserInfo userInfo = getUserInfoLU(userHandle); + final UserInfo userInfo = getUserInfoLU(userId); return userInfo != null && userInfo.name != null; } } @@ -3438,21 +3439,21 @@ public class UserManagerService extends IUserManager.Stub { } @Override - public long getUserCreationTime(int userHandle) { + public long getUserCreationTime(@UserIdInt int userId) { int callingUserId = UserHandle.getCallingUserId(); UserInfo userInfo = null; synchronized (mUsersLock) { - if (callingUserId == userHandle) { - userInfo = getUserInfoLU(userHandle); + if (callingUserId == userId) { + userInfo = getUserInfoLU(userId); } else { - UserInfo parent = getProfileParentLU(userHandle); + UserInfo parent = getProfileParentLU(userId); if (parent != null && parent.id == callingUserId) { - userInfo = getUserInfoLU(userHandle); + userInfo = getUserInfoLU(userId); } } } if (userInfo == null) { - throw new SecurityException("userHandle can only be the calling user or a managed " + throw new SecurityException("userId can only be the calling user or a managed " + "profile associated with this user"); } return userInfo.creationTime; @@ -3485,7 +3486,7 @@ public class UserManagerService extends IUserManager.Stub { * Called right before a user is started. This gives us a chance to prepare * app storage and apply any user restrictions. */ - public void onBeforeStartUser(int userId) { + public void onBeforeStartUser(@UserIdInt int userId) { UserInfo userInfo = getUserInfo(userId); if (userInfo == null) { return; @@ -3600,7 +3601,7 @@ public class UserManagerService extends IUserManager.Stub { } @Override - public void setSeedAccountData(int userId, String accountName, String accountType, + public void setSeedAccountData(@UserIdInt int userId, String accountName, String accountType, PersistableBundle accountOptions, boolean persist) { checkManageUsersPermission("Require MANAGE_USERS permission to set user seed data"); synchronized (mPackagesLock) { @@ -3881,20 +3882,20 @@ public class UserManagerService extends IUserManager.Stub { * @param userId * @return whether the user has been initialized yet */ - boolean isUserInitialized(int userId) { + boolean isUserInitialized(@UserIdInt int userId) { return mLocalService.isUserInitialized(userId); } private class LocalService extends UserManagerInternal { @Override - public void setDevicePolicyUserRestrictions(int userId, @Nullable Bundle restrictions, - boolean isDeviceOwner, int cameraRestrictionScope) { + public void setDevicePolicyUserRestrictions(@UserIdInt int userId, + @Nullable Bundle restrictions, boolean isDeviceOwner, int cameraRestrictionScope) { UserManagerService.this.setDevicePolicyUserRestrictionsInner(userId, restrictions, isDeviceOwner, cameraRestrictionScope); } @Override - public Bundle getBaseUserRestrictions(int userId) { + public Bundle getBaseUserRestrictions(@UserIdInt int userId) { synchronized (mRestrictionsLock) { return mBaseUserRestrictions.get(userId); } @@ -3902,7 +3903,7 @@ public class UserManagerService extends IUserManager.Stub { @Override public void setBaseUserRestrictionsByDpmsForMigration( - int userId, Bundle baseRestrictions) { + @UserIdInt int userId, Bundle baseRestrictions) { synchronized (mRestrictionsLock) { if (updateRestrictionsIfNeededLR( userId, new Bundle(baseRestrictions), mBaseUserRestrictions)) { @@ -3921,7 +3922,7 @@ public class UserManagerService extends IUserManager.Stub { } @Override - public boolean getUserRestriction(int userId, String key) { + public boolean getUserRestriction(@UserIdInt int userId, String key) { return getUserRestrictions(userId).getBoolean(key); } @@ -3947,14 +3948,14 @@ public class UserManagerService extends IUserManager.Stub { } @Override - public void setUserManaged(int userId, boolean isManaged) { + public void setUserManaged(@UserIdInt int userId, boolean isManaged) { synchronized (mUsersLock) { mIsUserManaged.put(userId, isManaged); } } @Override - public void setUserIcon(int userId, Bitmap bitmap) { + public void setUserIcon(@UserIdInt int userId, Bitmap bitmap) { long ident = Binder.clearCallingIdentity(); try { synchronized (mPackagesLock) { @@ -4011,7 +4012,7 @@ public class UserManagerService extends IUserManager.Stub { } @Override - public void onEphemeralUserStop(int userId) { + public void onEphemeralUserStop(@UserIdInt int userId) { synchronized (mUsersLock) { UserInfo userInfo = getUserInfoLU(userId); if (userInfo != null && userInfo.isEphemeral()) { @@ -4040,26 +4041,26 @@ public class UserManagerService extends IUserManager.Stub { } @Override - public boolean removeUserEvenWhenDisallowed(int userId) { + public boolean removeUserEvenWhenDisallowed(@UserIdInt int userId) { return removeUserUnchecked(userId); } @Override - public boolean isUserRunning(int userId) { + public boolean isUserRunning(@UserIdInt int userId) { synchronized (mUserStates) { return mUserStates.get(userId, -1) >= 0; } } @Override - public void setUserState(int userId, int userState) { + public void setUserState(@UserIdInt int userId, int userState) { synchronized (mUserStates) { mUserStates.put(userId, userState); } } @Override - public void removeUserState(int userId) { + public void removeUserState(@UserIdInt int userId) { synchronized (mUserStates) { mUserStates.delete(userId); } @@ -4071,7 +4072,7 @@ public class UserManagerService extends IUserManager.Stub { } @Override - public boolean isUserUnlockingOrUnlocked(int userId) { + public boolean isUserUnlockingOrUnlocked(@UserIdInt int userId) { int state; synchronized (mUserStates) { state = mUserStates.get(userId, -1); @@ -4085,7 +4086,7 @@ public class UserManagerService extends IUserManager.Stub { } @Override - public boolean isUserUnlocked(int userId) { + public boolean isUserUnlocked(@UserIdInt int userId) { int state; synchronized (mUserStates) { state = mUserStates.get(userId, -1); @@ -4098,12 +4099,12 @@ public class UserManagerService extends IUserManager.Stub { } @Override - public boolean isUserInitialized(int userId) { + public boolean isUserInitialized(@UserIdInt int userId) { return (getUserInfo(userId).flags & UserInfo.FLAG_INITIALIZED) != 0; } @Override - public boolean exists(int userId) { + public boolean exists(@UserIdInt int userId) { return getUserInfoNoChecks(userId) != null; } @@ -4147,7 +4148,7 @@ public class UserManagerService extends IUserManager.Stub { } @Override - public int getProfileParentId(int userId) { + public int getProfileParentId(@UserIdInt int userId) { synchronized (mUsersLock) { UserInfo profileParent = getProfileParentLU(userId); if (profileParent == null) { @@ -4165,7 +4166,7 @@ public class UserManagerService extends IUserManager.Stub { } @Override - public boolean hasUserRestriction(String restrictionKey, int userId) { + public boolean hasUserRestriction(String restrictionKey, @UserIdInt int userId) { if (!UserRestrictionsUtils.isValidRestriction(restrictionKey)) { return false; } @@ -4274,7 +4275,7 @@ public class UserManagerService extends IUserManager.Stub { * @param userId The parent user * @return */ - boolean hasManagedProfile(int userId) { + boolean hasManagedProfile(@UserIdInt int userId) { synchronized (mUsersLock) { UserInfo userInfo = getUserInfoLU(userId); final int userSize = mUsers.size(); diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index aecbca391735..b502bd54bfc5 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -3551,7 +3551,8 @@ public class DisplayPolicy { if (mScreenshotHelper != null) { mScreenshotHelper.takeScreenshot(screenshotType, mStatusBar != null && mStatusBar.isVisibleLw(), - mNavigationBar != null && mNavigationBar.isVisibleLw(), mHandler); + mNavigationBar != null && mNavigationBar.isVisibleLw(), + mHandler, null /* completionConsumer */); } } diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index 8c51452075b7..d7116d8bbd87 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -23,7 +23,6 @@ import static android.view.Display.INVALID_DISPLAY; import static com.android.server.am.ActivityManagerService.MY_PID; import static com.android.server.wm.ActivityStack.ActivityState.DESTROYED; import static com.android.server.wm.ActivityStack.ActivityState.DESTROYING; -import static com.android.server.wm.ActivityStack.ActivityState.INITIALIZING; import static com.android.server.wm.ActivityStack.ActivityState.PAUSED; import static com.android.server.wm.ActivityStack.ActivityState.PAUSING; import static com.android.server.wm.ActivityStack.ActivityState.RESUMED; @@ -556,14 +555,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio continue; } ActivityRecord topActivity = task.getTopActivity(); - if (topActivity == null) { - continue; - } - // If an activity has just been started it will not yet be visible, but - // is expected to be soon. We treat this as if it were already visible. - // This ensures a subsequent activity can be started even before this one - // becomes visible. - if (topActivity.visible || topActivity.isState(INITIALIZING)) { + if (topActivity != null && topActivity.visible) { return true; } } diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 8e679d44fe05..6dfbc36ce6f7 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -1050,7 +1050,7 @@ class WindowStateAnimator { // to prevent further updates until buffer latch. // We also need to freeze the Surface geometry until a buffer // comes in at the new size (normally position and crop are unfrozen). - // setGeometryAppliesWithResizeInTransaction accomplishes this for us. + // deferTransactionUntil accomplishes this for us. if (wasForceScaled && !mForceScaleUntilResize) { mSurfaceController.deferTransactionUntil(mSurfaceController.mSurfaceControl, mWin.getFrameNumber()); diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java index 8cede6c52914..6d813d106345 100644 --- a/services/core/java/com/android/server/wm/WindowSurfaceController.java +++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java @@ -245,10 +245,6 @@ class WindowSurfaceController { } } - void setGeometryAppliesWithResizeInTransaction(boolean recoveringMemory) { - mSurfaceControl.setGeometryAppliesWithResize(); - } - void setMatrixInTransaction(float dsdx, float dtdx, float dtdy, float dsdy, boolean recoveringMemory) { setMatrix(null, dsdx, dtdx, dtdy, dsdy, false); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/GlobalActionPerformerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/GlobalActionPerformerTest.java index e72e4601bbe8..c73be6f100cd 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/GlobalActionPerformerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/GlobalActionPerformerTest.java @@ -35,6 +35,8 @@ import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.function.Consumer; + /** * Tests for GlobalActionPerformer */ @@ -84,6 +86,6 @@ public class GlobalActionPerformerTest { AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT); verify(mMockScreenshotHelper).takeScreenshot( eq(android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN), anyBoolean(), - anyBoolean(), any(Handler.class)); + anyBoolean(), any(Handler.class), any()); } } diff --git a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java index 83dd69a24295..8d2a79b6b5db 100644 --- a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java +++ b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java @@ -185,11 +185,6 @@ public class StubTransaction extends SurfaceControl.Transaction { } @Override - public SurfaceControl.Transaction setGeometryAppliesWithResize(SurfaceControl sc) { - return this; - } - - @Override public SurfaceControl.Transaction setSecure(SurfaceControl sc, boolean isSecure) { return this; } diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java index 3a9979d78a55..356288047e15 100644 --- a/telephony/java/android/telephony/ims/feature/ImsFeature.java +++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java @@ -201,15 +201,20 @@ public abstract class ImsFeature { } /** - * Contains the capabilities defined and supported by an ImsFeature in the form of a bit mask. - * <p> - * Typically this class is not used directly, but rather extended in subclasses of + * Contains the IMS capabilities defined and supported by an ImsFeature in the form of a + * bit-mask. + * + * @deprecated This class is not used directly, but rather extended in subclasses of * {@link ImsFeature} to provide service specific capabilities. + * @see MmTelFeature.MmTelCapabilities * @hide */ - @SystemApi + // Not Actually deprecated, but we need to remove it from the @SystemApi surface. + @Deprecated + @SystemApi // SystemApi only because it was leaked through type usage in a previous release. public static class Capabilities { /** @deprecated Use getters and accessors instead. */ + // Not actually deprecated, but we need to remove it from the @SystemApi surface eventually. protected int mCapabilities = 0; /** diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java index 20c191da0550..ceb470491dc5 100644 --- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java +++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java @@ -242,8 +242,8 @@ public class MmTelFeature extends ImsFeature { * @param capabilities The capabilities that are supported for MmTel in the form of a * bitfield. */ - public MmTelCapabilities(int capabilities) { - mCapabilities = capabilities; + public MmTelCapabilities(@MmTelCapability int capabilities) { + super(capabilities); } @IntDef(flag = true, diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java index 2d867f9d754f..6c05bb8e8690 100644 --- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java +++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java @@ -43,6 +43,7 @@ import android.util.AtomicFile; import androidx.test.InstrumentationRegistry; +import com.android.server.PackageWatchdog.HealthCheckState; import com.android.server.PackageWatchdog.MonitoredPackage; import com.android.server.PackageWatchdog.PackageHealthObserver; import com.android.server.PackageWatchdog.PackageHealthObserverImpact; @@ -687,36 +688,36 @@ public class PackageWatchdogTest { // Verify transition: inactive -> active -> passed // Verify initially inactive - assertEquals(MonitoredPackage.STATE_INACTIVE, m1.getHealthCheckStateLocked()); + assertEquals(HealthCheckState.INACTIVE, m1.getHealthCheckStateLocked()); // Verify still inactive, until we #setHealthCheckActiveLocked - assertEquals(MonitoredPackage.STATE_INACTIVE, m1.handleElapsedTimeLocked(SHORT_DURATION)); + assertEquals(HealthCheckState.INACTIVE, m1.handleElapsedTimeLocked(SHORT_DURATION)); // Verify now active - assertEquals(MonitoredPackage.STATE_ACTIVE, m1.setHealthCheckActiveLocked(SHORT_DURATION)); + assertEquals(HealthCheckState.ACTIVE, m1.setHealthCheckActiveLocked(SHORT_DURATION)); // Verify now passed - assertEquals(MonitoredPackage.STATE_PASSED, m1.tryPassHealthCheckLocked()); + assertEquals(HealthCheckState.PASSED, m1.tryPassHealthCheckLocked()); // Verify transition: inactive -> active -> failed // Verify initially inactive - assertEquals(MonitoredPackage.STATE_INACTIVE, m2.getHealthCheckStateLocked()); + assertEquals(HealthCheckState.INACTIVE, m2.getHealthCheckStateLocked()); // Verify now active - assertEquals(MonitoredPackage.STATE_ACTIVE, m2.setHealthCheckActiveLocked(SHORT_DURATION)); + assertEquals(HealthCheckState.ACTIVE, m2.setHealthCheckActiveLocked(SHORT_DURATION)); // Verify now failed - assertEquals(MonitoredPackage.STATE_FAILED, m2.handleElapsedTimeLocked(SHORT_DURATION)); + assertEquals(HealthCheckState.FAILED, m2.handleElapsedTimeLocked(SHORT_DURATION)); // Verify transition: inactive -> failed // Verify initially inactive - assertEquals(MonitoredPackage.STATE_INACTIVE, m3.getHealthCheckStateLocked()); + assertEquals(HealthCheckState.INACTIVE, m3.getHealthCheckStateLocked()); // Verify now failed because package expired - assertEquals(MonitoredPackage.STATE_FAILED, m3.handleElapsedTimeLocked(LONG_DURATION)); + assertEquals(HealthCheckState.FAILED, m3.handleElapsedTimeLocked(LONG_DURATION)); // Verify remains failed even when asked to pass - assertEquals(MonitoredPackage.STATE_FAILED, m3.tryPassHealthCheckLocked()); + assertEquals(HealthCheckState.FAILED, m3.tryPassHealthCheckLocked()); // Verify transition: passed - assertEquals(MonitoredPackage.STATE_PASSED, m4.getHealthCheckStateLocked()); + assertEquals(HealthCheckState.PASSED, m4.getHealthCheckStateLocked()); // Verify remains passed even if health check fails - assertEquals(MonitoredPackage.STATE_PASSED, m4.handleElapsedTimeLocked(SHORT_DURATION)); + assertEquals(HealthCheckState.PASSED, m4.handleElapsedTimeLocked(SHORT_DURATION)); // Verify remains passed even if package expires - assertEquals(MonitoredPackage.STATE_PASSED, m4.handleElapsedTimeLocked(LONG_DURATION)); + assertEquals(HealthCheckState.PASSED, m4.handleElapsedTimeLocked(LONG_DURATION)); } @Test diff --git a/tests/RollbackTest/Android.bp b/tests/RollbackTest/Android.bp index 2bd5931eccbe..231d045bd817 100644 --- a/tests/RollbackTest/Android.bp +++ b/tests/RollbackTest/Android.bp @@ -31,9 +31,9 @@ java_test_host { } java_test_host { - name: "SecondaryUserRollbackTest", - srcs: ["SecondaryUserRollbackTest/src/**/*.java"], + name: "MultiUserRollbackTest", + srcs: ["MultiUserRollbackTest/src/**/*.java"], libs: ["tradefed"], test_suites: ["general-tests"], - test_config: "SecondaryUserRollbackTest.xml", + test_config: "MultiUserRollbackTest.xml", } diff --git a/tests/RollbackTest/SecondaryUserRollbackTest.xml b/tests/RollbackTest/MultiUserRollbackTest.xml index 6b3f05c42983..41cec461c377 100644 --- a/tests/RollbackTest/SecondaryUserRollbackTest.xml +++ b/tests/RollbackTest/MultiUserRollbackTest.xml @@ -13,17 +13,12 @@ See the License for the specific language governing permissions and limitations under the License. --> -<configuration description="Runs the rollback test from a secondary user"> - <option name="test-suite-tag" value="SecondaryUserRollbackTest" /> - <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> - <option name="cleanup-apks" value="true" /> - <option name="test-file-name" value="RollbackTest.apk" /> - </target_preparer> +<configuration description="Runs rollback tests for multiple users"> + <option name="test-suite-tag" value="MultiUserRollbackTest" /> <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> <option name="run-command" value="pm uninstall com.android.cts.install.lib.testapp.A" /> - <option name="run-command" value="pm uninstall com.android.cts.install.lib.testapp.B" /> </target_preparer> <test class="com.android.tradefed.testtype.HostTest" > - <option name="class" value="com.android.tests.rollback.host.SecondaryUserRollbackTest" /> + <option name="class" value="com.android.tests.rollback.host.MultiUserRollbackTest" /> </test> </configuration> diff --git a/tests/RollbackTest/SecondaryUserRollbackTest/src/com/android/tests/rollback/host/SecondaryUserRollbackTest.java b/tests/RollbackTest/MultiUserRollbackTest/src/com/android/tests/rollback/host/MultiUserRollbackTest.java index 11a0fbb93366..52f6eba4072b 100644 --- a/tests/RollbackTest/SecondaryUserRollbackTest/src/com/android/tests/rollback/host/SecondaryUserRollbackTest.java +++ b/tests/RollbackTest/MultiUserRollbackTest/src/com/android/tests/rollback/host/MultiUserRollbackTest.java @@ -28,13 +28,12 @@ import org.junit.Test; import org.junit.runner.RunWith; /** - * Runs rollback tests from a secondary user. + * Runs rollback tests for multiple users. */ @RunWith(DeviceJUnit4ClassRunner.class) -public class SecondaryUserRollbackTest extends BaseHostJUnit4Test { - private static final int SYSTEM_USER_ID = 0; +public class MultiUserRollbackTest extends BaseHostJUnit4Test { // The user that was running originally when the test starts. - private int mOriginalUser = SYSTEM_USER_ID; + private int mOriginalUserId; private int mSecondaryUserId = -1; private static final long SWITCH_USER_COMPLETED_NUMBER_OF_POLLS = 60; private static final long SWITCH_USER_COMPLETED_POLL_INTERVAL_IN_MILLIS = 1000; @@ -42,23 +41,45 @@ public class SecondaryUserRollbackTest extends BaseHostJUnit4Test { @After public void tearDown() throws Exception { - getDevice().switchUser(mOriginalUser); + getDevice().switchUser(mOriginalUserId); getDevice().executeShellCommand("pm uninstall com.android.cts.install.lib.testapp.A"); - getDevice().executeShellCommand("pm uninstall com.android.cts.install.lib.testapp.B"); removeSecondaryUserIfNecessary(); } @Before public void setup() throws Exception { + mOriginalUserId = getDevice().getCurrentUser(); + installPackageAsUser("RollbackTest.apk", true, mOriginalUserId); createAndSwitchToSecondaryUserIfNecessary(); - installPackageAsUser("RollbackTest.apk", true, mSecondaryUserId, "--user current"); + installPackageAsUser("RollbackTest.apk", true, mSecondaryUserId); } @Test - public void testBasic() throws Exception { - assertTrue(runDeviceTests("com.android.tests.rollback", - "com.android.tests.rollback.RollbackTest", - "testBasic")); + public void testBasicForSecondaryUser() throws Exception { + runPhaseForUsers("testBasic", mSecondaryUserId); + } + + @Test + public void testMultipleUsers() throws Exception { + runPhaseForUsers("testMultipleUsersInstallV1", mOriginalUserId, mSecondaryUserId); + runPhaseForUsers("testMultipleUsersUpgradeToV2", mOriginalUserId); + runPhaseForUsers("testMultipleUsersUpdateUserData", mOriginalUserId, mSecondaryUserId); + switchToUser(mOriginalUserId); + getDevice().executeShellCommand("pm rollback-app com.android.cts.install.lib.testapp.A"); + runPhaseForUsers("testMultipleUsersVerifyUserdataRollback", mOriginalUserId, + mSecondaryUserId); + } + + /** + * Run the phase for the given user ids, in the order they are given. + */ + private void runPhaseForUsers(String phase, int... userIds) throws Exception { + for (int userId: userIds) { + switchToUser(userId); + assertTrue(runDeviceTests("com.android.tests.rollback", + "com.android.tests.rollback.MultiUserRollbackTest", + phase)); + } } private void removeSecondaryUserIfNecessary() throws Exception { @@ -70,19 +91,23 @@ public class SecondaryUserRollbackTest extends BaseHostJUnit4Test { private void createAndSwitchToSecondaryUserIfNecessary() throws Exception { if (mSecondaryUserId == -1) { - mOriginalUser = getDevice().getCurrentUser(); - mSecondaryUserId = getDevice().createUser("SecondaryUserRollbackTest_User"); - assertTrue(getDevice().switchUser(mSecondaryUserId)); - // give time for user to be switched - waitForSwitchUserCompleted(mSecondaryUserId); + mOriginalUserId = getDevice().getCurrentUser(); + mSecondaryUserId = getDevice().createUser("MultiUserRollbackTest_User" + + System.currentTimeMillis()); + switchToUser(mSecondaryUserId); } } - private void waitForSwitchUserCompleted(int userId) throws Exception { + private void switchToUser(int userId) throws Exception { + if (getDevice().getCurrentUser() == userId) { + return; + } + + assertTrue(getDevice().switchUser(userId)); for (int i = 0; i < SWITCH_USER_COMPLETED_NUMBER_OF_POLLS; ++i) { - String logs = getDevice().executeAdbCommand("logcat", "-v", "brief", "-d", - "ActivityManager:D"); - if (logs.contains("Posting BOOT_COMPLETED user #" + userId)) { + String userState = getDevice().executeShellCommand("am get-started-user-state " + + userId); + if (userState.contains("RUNNING_UNLOCKED")) { return; } Thread.sleep(SWITCH_USER_COMPLETED_POLL_INTERVAL_IN_MILLIS); diff --git a/tests/RollbackTest/RollbackTest.xml b/tests/RollbackTest/RollbackTest.xml index 70cd86783d6d..a14b01c57b1b 100644 --- a/tests/RollbackTest/RollbackTest.xml +++ b/tests/RollbackTest/RollbackTest.xml @@ -22,8 +22,9 @@ <option name="package" value="com.android.tests.rollback" /> <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> - <!-- Exclude the StagedRollbackTest tests, which needs to be specially - driven from the StagedRollbackTest host test --> + <!-- Exclude the StagedRollbackTest and MultiUserRollbackTest tests, which need to be + specially driven from the StagedRollbackTest and MultiUserRollbackTest host test --> <option name="exclude-filter" value="com.android.tests.rollback.StagedRollbackTest" /> + <option name="exclude-filter" value="com.android.tests.rollback.MultiUserRollbackTest" /> </test> </configuration> diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/MultiUserRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/MultiUserRollbackTest.java new file mode 100644 index 000000000000..0ffe041b0377 --- /dev/null +++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/MultiUserRollbackTest.java @@ -0,0 +1,111 @@ +/* + * 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.tests.rollback; + +import static com.android.cts.rollback.lib.RollbackInfoSubject.assertThat; +import static com.android.cts.rollback.lib.RollbackUtils.getUniqueRollbackInfoForPackage; + +import static com.google.common.truth.Truth.assertThat; + +import android.Manifest; +import android.content.rollback.RollbackInfo; +import android.content.rollback.RollbackManager; + +import com.android.cts.install.lib.Install; +import com.android.cts.install.lib.InstallUtils; +import com.android.cts.install.lib.TestApp; +import com.android.cts.rollback.lib.Rollback; +import com.android.cts.rollback.lib.RollbackUtils; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + + +@RunWith(JUnit4.class) +public class MultiUserRollbackTest { + + @Before + public void adoptShellPermissions() { + InstallUtils.adoptShellPermissionIdentity( + Manifest.permission.INSTALL_PACKAGES, + Manifest.permission.DELETE_PACKAGES, + Manifest.permission.TEST_MANAGE_ROLLBACKS, + Manifest.permission.MANAGE_ROLLBACKS); + } + + @After + public void dropShellPermissions() { + InstallUtils.dropShellPermissionIdentity(); + } + + @Test + public void testBasic() throws Exception { + new RollbackTest().testBasic(); + } + + /** + * Install version 1 of the test app. This method is run for both users. + */ + @Test + public void testMultipleUsersInstallV1() throws Exception { + assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1); + Install.single(TestApp.A1).commit(); + assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1); + InstallUtils.processUserData(TestApp.A); + } + + /** + * Upgrade the test app to version 2. This method should only run once as the system user, + * and will update the app for both users. + */ + @Test + public void testMultipleUsersUpgradeToV2() throws Exception { + RollbackManager rm = RollbackUtils.getRollbackManager(); + assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1); + Install.single(TestApp.A2).setEnableRollback().commit(); + assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2); + RollbackInfo rollback = getUniqueRollbackInfoForPackage( + rm.getAvailableRollbacks(), TestApp.A); + assertThat(rollback).isNotNull(); + assertThat(rollback).packagesContainsExactly( + Rollback.from(TestApp.A2).to(TestApp.A1)); + } + + /** + * This method is run for both users. Assert that the test app has upgraded for both users, and + * update their userdata to reflect this new version. + */ + @Test + public void testMultipleUsersUpdateUserData() { + assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2); + InstallUtils.processUserData(TestApp.A); + } + + /** + * The system will have rolled back the test app at this stage. Verify that the rollback has + * taken place, and that the userdata has been correctly rolled back. This method is run for + * both users. + */ + @Test + public void testMultipleUsersVerifyUserdataRollback() { + assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1); + InstallUtils.processUserData(TestApp.A); + } +} diff --git a/tests/RollbackTest/TEST_MAPPING b/tests/RollbackTest/TEST_MAPPING index 7ae03e68decc..fefde5b4be12 100644 --- a/tests/RollbackTest/TEST_MAPPING +++ b/tests/RollbackTest/TEST_MAPPING @@ -7,7 +7,7 @@ "name": "StagedRollbackTest" }, { - "name": "SecondaryUserRollbackTest" + "name": "MultiUserRollbackTest" } ] } diff --git a/tools/protologtool/Android.bp b/tools/protologtool/Android.bp new file mode 100644 index 000000000000..a86c226c2179 --- /dev/null +++ b/tools/protologtool/Android.bp @@ -0,0 +1,28 @@ +java_binary_host { + name: "protologtool", + manifest: "manifest.txt", + srcs: [ + "src/**/*.kt", + ], + static_libs: [ + "javaparser", + "windowmanager-log-proto", + "jsonlib", + ], +} + +java_test_host { + name: "protologtool-tests", + test_suites: ["general-tests"], + srcs: [ + "src/**/*.kt", + "tests/**/*.kt", + ], + static_libs: [ + "javaparser", + "windowmanager-log-proto", + "jsonlib", + "junit", + "mockito", + ], +} diff --git a/tools/protologtool/README.md b/tools/protologtool/README.md new file mode 100644 index 000000000000..3439357af598 --- /dev/null +++ b/tools/protologtool/README.md @@ -0,0 +1,106 @@ +# ProtoLogTool + +Code transformation tool and viewer for ProtoLog. + +## What does it do? + +ProtoLogTool incorporates three different modes of operation: + +### Code transformation + +Command: `process <protolog class path> <protolog implementation class path> + <protolog groups class path> <config.jar> [<input.java>] <output.srcjar>` + +In this mode ProtoLogTool transforms every ProtoLog logging call in form of: +```java +ProtoLog.x(ProtoLogGroup.GROUP_NAME, "Format string %d %s", value1, value2); +``` +into: +```java +if (GROUP_NAME.isLogToAny()) { + ProtoLogImpl.x(ProtoLogGroup.GROUP_NAME, 123456, "Format string %d %s or null", value1, value2); +} +``` +where `ProtoLog`, `ProtoLogImpl` and `ProtoLogGroup` are the classes provided as arguments + (can be imported, static imported or full path, wildcard imports are not allowed) and, `x` is the + logging method. The transformation is done on the source level. A hash is generated from the format + string and log level and inserted after the `ProtoLogGroup` argument. The format string is replaced + by `null` if `ProtoLogGroup.GROUP_NAME.isLogToLogcat()` returns false. If `ProtoLogGroup.GROUP_NAME.isEnabled()` + returns false the log statement is removed entirely from the resultant code. + +Input is provided as a list of java source file names. Transformed source is saved to a single +source jar file. The ProtoLogGroup class with all dependencies should be provided as a compiled +jar file (config.jar). + +### Viewer config generation + +Command: `viewerconf <protolog class path> <protolog implementation class path +<protolog groups class path> <config.jar> [<input.java>] <output.json>` + +This command is similar in it's syntax to the previous one, only instead of creating a processed source jar +it writes a viewer configuration file with following schema: +```json +{ + "version": "1.0.0", + "messages": { + "123456": { + "message": "Format string %d %s", + "level": "ERROR", + "group": "GROUP_NAME" + }, + }, + "groups": { + "GROUP_NAME": { + "tag": "TestLog" + } + } +} + +``` + +### Binary log viewing + +Command: `read <viewer.json> <wm_log.pb>` + +Reads the binary ProtoLog log file and outputs a human-readable LogCat-like text log. + +## What is ProtoLog? + +ProtoLog is a logging system created for the WindowManager project. It allows both binary and text logging +and is tunable in runtime. It consists of 3 different submodules: +* logging system built-in the Android app, +* log viewer for reading binary logs, +* a code processing tool. + +ProtoLog is designed to reduce both application size (and by that memory usage) and amount of resources needed +for logging. This is achieved by replacing log message strings with their hashes and only loading to memory/writing +full log messages when necessary. + +### Text logging + +For text-based logs Android LogCat is used as a backend. Message strings are loaded from a viewer config +located on the device when needed. + +### Binary logging + +Binary logs are saved as Protocol Buffers file. They can be read using the ProtoLog tool or specialised +viewer like Winscope. + +## How to use ProtoLog? + +### Adding a new logging group or log statement + +To add a new ProtoLogGroup simple create a new enum ProtoLogGroup member with desired parameters. + +To add a new logging statement just add a new call to ProtoLog.x where x is a log level. + +After doing any changes to logging groups or statements you should run `make update-protolog` to update +viewer configuration saved in the code repository. + +## How to change settings on device in runtime? +Use the `adb shell su root cmd window logging` command. To get help just type +`adb shell su root cmd window logging help`. + + + + diff --git a/tools/protologtool/TEST_MAPPING b/tools/protologtool/TEST_MAPPING new file mode 100644 index 000000000000..52b12dc26be9 --- /dev/null +++ b/tools/protologtool/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "presubmit": [ + { + "name": "protologtool-tests" + } + ] +} diff --git a/tools/protologtool/manifest.txt b/tools/protologtool/manifest.txt new file mode 100644 index 000000000000..f5e53c450f2a --- /dev/null +++ b/tools/protologtool/manifest.txt @@ -0,0 +1 @@ +Main-class: com.android.protologtool.ProtoLogTool diff --git a/tools/protologtool/src/com/android/protologtool/CodeUtils.kt b/tools/protologtool/src/com/android/protologtool/CodeUtils.kt new file mode 100644 index 000000000000..facca6290c91 --- /dev/null +++ b/tools/protologtool/src/com/android/protologtool/CodeUtils.kt @@ -0,0 +1,135 @@ +/* + * 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.protologtool + +import com.github.javaparser.StaticJavaParser +import com.github.javaparser.ast.CompilationUnit +import com.github.javaparser.ast.ImportDeclaration +import com.github.javaparser.ast.NodeList +import com.github.javaparser.ast.expr.BinaryExpr +import com.github.javaparser.ast.expr.Expression +import com.github.javaparser.ast.expr.MethodCallExpr +import com.github.javaparser.ast.expr.SimpleName +import com.github.javaparser.ast.expr.StringLiteralExpr +import com.github.javaparser.ast.expr.TypeExpr +import com.github.javaparser.ast.type.PrimitiveType +import com.github.javaparser.ast.type.Type + +object CodeUtils { + /** + * Returns a stable hash of a string. + * We reimplement String::hashCode() for readability reasons. + */ + fun hash(str: String, level: LogLevel): Int { + return (level.name + str).map { c -> c.toInt() }.reduce { h, c -> h * 31 + c } + } + + fun isWildcardStaticImported(code: CompilationUnit, className: String): Boolean { + return code.findAll(ImportDeclaration::class.java) + .any { im -> im.isStatic && im.isAsterisk && im.name.toString() == className } + } + + fun isClassImportedOrSamePackage(code: CompilationUnit, className: String): Boolean { + val packageName = className.substringBeforeLast('.') + return code.packageDeclaration.isPresent && + code.packageDeclaration.get().nameAsString == packageName || + code.findAll(ImportDeclaration::class.java) + .any { im -> + !im.isStatic && + ((!im.isAsterisk && im.name.toString() == className) || + (im.isAsterisk && im.name.toString() == packageName)) + } + } + + fun staticallyImportedMethods(code: CompilationUnit, className: String): Set<String> { + return code.findAll(ImportDeclaration::class.java) + .filter { im -> + im.isStatic && + im.name.toString().substringBeforeLast('.') == className + } + .map { im -> im.name.toString().substringAfterLast('.') }.toSet() + } + + fun concatMultilineString(expr: Expression): String { + return when (expr) { + is StringLiteralExpr -> expr.asString() + is BinaryExpr -> when { + expr.operator == BinaryExpr.Operator.PLUS -> + concatMultilineString(expr.left) + concatMultilineString(expr.right) + else -> throw InvalidProtoLogCallException( + "messageString must be a string literal " + + "or concatenation of string literals.", expr) + } + else -> throw InvalidProtoLogCallException("messageString must be a string literal " + + "or concatenation of string literals.", expr) + } + } + + enum class LogDataTypes( + val type: Type, + val toType: (Expression) -> Expression = { expr -> expr } + ) { + // When adding new LogDataType make sure to update {@code logDataTypesToBitMask} accordingly + STRING(StaticJavaParser.parseClassOrInterfaceType("String"), + { expr -> + MethodCallExpr(TypeExpr(StaticJavaParser.parseClassOrInterfaceType("String")), + SimpleName("valueOf"), NodeList(expr)) + }), + LONG(PrimitiveType.longType()), + DOUBLE(PrimitiveType.doubleType()), + BOOLEAN(PrimitiveType.booleanType()); + } + + fun parseFormatString(messageString: String): List<LogDataTypes> { + val types = mutableListOf<LogDataTypes>() + var i = 0 + while (i < messageString.length) { + if (messageString[i] == '%') { + if (i + 1 >= messageString.length) { + throw InvalidFormatStringException("Invalid format string in config") + } + when (messageString[i + 1]) { + 'b' -> types.add(CodeUtils.LogDataTypes.BOOLEAN) + 'd', 'o', 'x' -> types.add(CodeUtils.LogDataTypes.LONG) + 'f', 'e', 'g' -> types.add(CodeUtils.LogDataTypes.DOUBLE) + 's' -> types.add(CodeUtils.LogDataTypes.STRING) + '%' -> { + } + else -> throw InvalidFormatStringException("Invalid format string field" + + " %${messageString[i + 1]}") + } + i += 2 + } else { + i += 1 + } + } + return types + } + + fun logDataTypesToBitMask(types: List<LogDataTypes>): Int { + if (types.size > 16) { + throw InvalidFormatStringException("Too many log call parameters " + + "- max 16 parameters supported") + } + var mask = 0 + types.forEachIndexed { idx, type -> + val x = LogDataTypes.values().indexOf(type) + mask = mask or (x shl (idx * 2)) + } + return mask + } +} diff --git a/tools/protologtool/src/com/android/protologtool/CommandOptions.kt b/tools/protologtool/src/com/android/protologtool/CommandOptions.kt new file mode 100644 index 000000000000..df49e1566fbc --- /dev/null +++ b/tools/protologtool/src/com/android/protologtool/CommandOptions.kt @@ -0,0 +1,205 @@ +/* + * 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.protologtool + +import java.util.regex.Pattern + +class CommandOptions(args: Array<String>) { + companion object { + const val TRANSFORM_CALLS_CMD = "transform-protolog-calls" + const val GENERATE_CONFIG_CMD = "generate-viewer-config" + const val READ_LOG_CMD = "read-log" + private val commands = setOf(TRANSFORM_CALLS_CMD, GENERATE_CONFIG_CMD, READ_LOG_CMD) + + private const val PROTOLOG_CLASS_PARAM = "--protolog-class" + private const val PROTOLOGIMPL_CLASS_PARAM = "--protolog-impl-class" + private const val PROTOLOGGROUP_CLASS_PARAM = "--loggroups-class" + private const val PROTOLOGGROUP_JAR_PARAM = "--loggroups-jar" + private const val VIEWER_CONFIG_JSON_PARAM = "--viewer-conf" + private const val OUTPUT_SOURCE_JAR_PARAM = "--output-srcjar" + private val parameters = setOf(PROTOLOG_CLASS_PARAM, PROTOLOGIMPL_CLASS_PARAM, + PROTOLOGGROUP_CLASS_PARAM, PROTOLOGGROUP_JAR_PARAM, VIEWER_CONFIG_JSON_PARAM, + OUTPUT_SOURCE_JAR_PARAM) + + val USAGE = """ + Usage: ${Constants.NAME} <command> [<args>] + Available commands: + + $TRANSFORM_CALLS_CMD $PROTOLOG_CLASS_PARAM <class name> $PROTOLOGIMPL_CLASS_PARAM + <class name> $PROTOLOGGROUP_CLASS_PARAM <class name> $PROTOLOGGROUP_JAR_PARAM + <config.jar> $OUTPUT_SOURCE_JAR_PARAM <output.srcjar> [<input.java>] + - processes java files replacing stub calls with logging code. + + $GENERATE_CONFIG_CMD $PROTOLOG_CLASS_PARAM <class name> $PROTOLOGGROUP_CLASS_PARAM + <class name> $PROTOLOGGROUP_JAR_PARAM <config.jar> $VIEWER_CONFIG_JSON_PARAM + <viewer.json> [<input.java>] + - creates viewer config file from given java files. + + $READ_LOG_CMD $VIEWER_CONFIG_JSON_PARAM <viewer.json> <wm_log.pb> + - translates a binary log to a readable format. + """.trimIndent() + + private fun validateClassName(name: String): String { + if (!Pattern.matches("^([a-z]+[A-Za-z0-9]*\\.)+([A-Za-z0-9]+)$", name)) { + throw InvalidCommandException("Invalid class name $name") + } + return name + } + + private fun getParam(paramName: String, params: Map<String, String>): String { + if (!params.containsKey(paramName)) { + throw InvalidCommandException("Param $paramName required") + } + return params.getValue(paramName) + } + + private fun validateNotSpecified(paramName: String, params: Map<String, String>): String { + if (params.containsKey(paramName)) { + throw InvalidCommandException("Unsupported param $paramName") + } + return "" + } + + private fun validateJarName(name: String): String { + if (!name.endsWith(".jar")) { + throw InvalidCommandException("Jar file required, got $name instead") + } + return name + } + + private fun validateSrcJarName(name: String): String { + if (!name.endsWith(".srcjar")) { + throw InvalidCommandException("Source jar file required, got $name instead") + } + return name + } + + private fun validateJSONName(name: String): String { + if (!name.endsWith(".json")) { + throw InvalidCommandException("Json file required, got $name instead") + } + return name + } + + private fun validateJavaInputList(list: List<String>): List<String> { + if (list.isEmpty()) { + throw InvalidCommandException("No java source input files") + } + list.forEach { name -> + if (!name.endsWith(".java")) { + throw InvalidCommandException("Not a java source file $name") + } + } + return list + } + + private fun validateLogInputList(list: List<String>): String { + if (list.isEmpty()) { + throw InvalidCommandException("No log input file") + } + if (list.size > 1) { + throw InvalidCommandException("Only one log input file allowed") + } + return list[0] + } + } + + val protoLogClassNameArg: String + val protoLogGroupsClassNameArg: String + val protoLogImplClassNameArg: String + val protoLogGroupsJarArg: String + val viewerConfigJsonArg: String + val outputSourceJarArg: String + val logProtofileArg: String + val javaSourceArgs: List<String> + val command: String + + init { + if (args.isEmpty()) { + throw InvalidCommandException("No command specified.") + } + command = args[0] + if (command !in commands) { + throw InvalidCommandException("Unknown command.") + } + + val params: MutableMap<String, String> = mutableMapOf() + val inputFiles: MutableList<String> = mutableListOf() + + var idx = 1 + while (idx < args.size) { + if (args[idx].startsWith("--")) { + if (idx + 1 >= args.size) { + throw InvalidCommandException("No value for ${args[idx]}") + } + if (args[idx] !in parameters) { + throw InvalidCommandException("Unknown parameter ${args[idx]}") + } + if (args[idx + 1].startsWith("--")) { + throw InvalidCommandException("No value for ${args[idx]}") + } + if (params.containsKey(args[idx])) { + throw InvalidCommandException("Duplicated parameter ${args[idx]}") + } + params[args[idx]] = args[idx + 1] + idx += 2 + } else { + inputFiles.add(args[idx]) + idx += 1 + } + } + + when (command) { + TRANSFORM_CALLS_CMD -> { + protoLogClassNameArg = validateClassName(getParam(PROTOLOG_CLASS_PARAM, params)) + protoLogGroupsClassNameArg = validateClassName(getParam(PROTOLOGGROUP_CLASS_PARAM, + params)) + protoLogImplClassNameArg = validateClassName(getParam(PROTOLOGIMPL_CLASS_PARAM, + params)) + protoLogGroupsJarArg = validateJarName(getParam(PROTOLOGGROUP_JAR_PARAM, params)) + viewerConfigJsonArg = validateNotSpecified(VIEWER_CONFIG_JSON_PARAM, params) + outputSourceJarArg = validateSrcJarName(getParam(OUTPUT_SOURCE_JAR_PARAM, params)) + javaSourceArgs = validateJavaInputList(inputFiles) + logProtofileArg = "" + } + GENERATE_CONFIG_CMD -> { + protoLogClassNameArg = validateClassName(getParam(PROTOLOG_CLASS_PARAM, params)) + protoLogGroupsClassNameArg = validateClassName(getParam(PROTOLOGGROUP_CLASS_PARAM, + params)) + protoLogImplClassNameArg = validateNotSpecified(PROTOLOGIMPL_CLASS_PARAM, params) + protoLogGroupsJarArg = validateJarName(getParam(PROTOLOGGROUP_JAR_PARAM, params)) + viewerConfigJsonArg = validateJSONName(getParam(VIEWER_CONFIG_JSON_PARAM, params)) + outputSourceJarArg = validateNotSpecified(OUTPUT_SOURCE_JAR_PARAM, params) + javaSourceArgs = validateJavaInputList(inputFiles) + logProtofileArg = "" + } + READ_LOG_CMD -> { + protoLogClassNameArg = validateNotSpecified(PROTOLOG_CLASS_PARAM, params) + protoLogGroupsClassNameArg = validateNotSpecified(PROTOLOGGROUP_CLASS_PARAM, params) + protoLogImplClassNameArg = validateNotSpecified(PROTOLOGIMPL_CLASS_PARAM, params) + protoLogGroupsJarArg = validateNotSpecified(PROTOLOGGROUP_JAR_PARAM, params) + viewerConfigJsonArg = validateJSONName(getParam(VIEWER_CONFIG_JSON_PARAM, params)) + outputSourceJarArg = validateNotSpecified(OUTPUT_SOURCE_JAR_PARAM, params) + javaSourceArgs = listOf() + logProtofileArg = validateLogInputList(inputFiles) + } + else -> { + throw InvalidCommandException("Unknown command.") + } + } + } +} diff --git a/tools/protologtool/src/com/android/protologtool/Constants.kt b/tools/protologtool/src/com/android/protologtool/Constants.kt new file mode 100644 index 000000000000..2ccfc4d20182 --- /dev/null +++ b/tools/protologtool/src/com/android/protologtool/Constants.kt @@ -0,0 +1,27 @@ +/* + * 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.protologtool + +object Constants { + const val NAME = "protologtool" + const val VERSION = "1.0.0" + const val IS_ENABLED_METHOD = "isEnabled" + const val IS_LOG_TO_LOGCAT_METHOD = "isLogToLogcat" + const val IS_LOG_TO_ANY_METHOD = "isLogToAny" + const val GET_TAG_METHOD = "getTag" + const val ENUM_VALUES_METHOD = "values" +} diff --git a/tools/protologtool/src/com/android/protologtool/LogGroup.kt b/tools/protologtool/src/com/android/protologtool/LogGroup.kt new file mode 100644 index 000000000000..42a37a26e08a --- /dev/null +++ b/tools/protologtool/src/com/android/protologtool/LogGroup.kt @@ -0,0 +1,24 @@ +/* + * 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.protologtool + +data class LogGroup( + val name: String, + val enabled: Boolean, + val textEnabled: Boolean, + val tag: String +) diff --git a/tools/protologtool/src/com/android/protologtool/LogLevel.kt b/tools/protologtool/src/com/android/protologtool/LogLevel.kt new file mode 100644 index 000000000000..dc29557ef440 --- /dev/null +++ b/tools/protologtool/src/com/android/protologtool/LogLevel.kt @@ -0,0 +1,37 @@ +/* + * 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.protologtool + +import com.github.javaparser.ast.Node + +enum class LogLevel { + DEBUG, VERBOSE, INFO, WARN, ERROR, WTF; + + companion object { + fun getLevelForMethodName(name: String, node: Node): LogLevel { + return when (name) { + "d" -> DEBUG + "v" -> VERBOSE + "i" -> INFO + "w" -> WARN + "e" -> ERROR + "wtf" -> WTF + else -> throw InvalidProtoLogCallException("Unknown log level $name", node) + } + } + } +} diff --git a/tools/protologtool/src/com/android/protologtool/LogParser.kt b/tools/protologtool/src/com/android/protologtool/LogParser.kt new file mode 100644 index 000000000000..4d0eb0e4a705 --- /dev/null +++ b/tools/protologtool/src/com/android/protologtool/LogParser.kt @@ -0,0 +1,112 @@ +/* + * 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.protologtool + +import com.android.json.stream.JsonReader +import com.android.server.wm.ProtoLogMessage +import com.android.server.wm.WindowManagerLogFileProto +import java.io.BufferedReader +import java.io.InputStream +import java.io.InputStreamReader +import java.io.PrintStream +import java.lang.Exception +import java.text.SimpleDateFormat +import java.util.Date +import java.util.Locale + +/** + * Implements a simple parser/viewer for binary ProtoLog logs. + * A binary log is translated into Android "LogCat"-like text log. + */ +class LogParser(private val configParser: ViewerConfigParser) { + companion object { + private val dateFormat = SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US) + private val magicNumber = + WindowManagerLogFileProto.MagicNumber.MAGIC_NUMBER_H.number.toLong() shl 32 or + WindowManagerLogFileProto.MagicNumber.MAGIC_NUMBER_L.number.toLong() + } + + private fun printTime(time: Long, offset: Long, ps: PrintStream) { + ps.print(dateFormat.format(Date(time / 1000000 + offset)) + " ") + } + + private fun printFormatted( + protoLogMessage: ProtoLogMessage, + configEntry: ViewerConfigParser.ConfigEntry, + ps: PrintStream + ) { + val strParmIt = protoLogMessage.strParamsList.iterator() + val longParamsIt = protoLogMessage.sint64ParamsList.iterator() + val doubleParamsIt = protoLogMessage.doubleParamsList.iterator() + val boolParamsIt = protoLogMessage.booleanParamsList.iterator() + val args = mutableListOf<Any>() + val format = configEntry.messageString + val argTypes = CodeUtils.parseFormatString(format) + try { + argTypes.forEach { + when (it) { + CodeUtils.LogDataTypes.BOOLEAN -> args.add(boolParamsIt.next()) + CodeUtils.LogDataTypes.LONG -> args.add(longParamsIt.next()) + CodeUtils.LogDataTypes.DOUBLE -> args.add(doubleParamsIt.next()) + CodeUtils.LogDataTypes.STRING -> args.add(strParmIt.next()) + } + } + } catch (ex: NoSuchElementException) { + throw InvalidFormatStringException("Invalid format string in config", ex) + } + if (strParmIt.hasNext() || longParamsIt.hasNext() || + doubleParamsIt.hasNext() || boolParamsIt.hasNext()) { + throw RuntimeException("Invalid format string in config - no enough matchers") + } + val formatted = format.format(*(args.toTypedArray())) + ps.print("${configEntry.level} ${configEntry.tag}: $formatted\n") + } + + private fun printUnformatted(protoLogMessage: ProtoLogMessage, ps: PrintStream, tag: String) { + ps.println("$tag: ${protoLogMessage.messageHash} - ${protoLogMessage.strParamsList}" + + " ${protoLogMessage.sint64ParamsList} ${protoLogMessage.doubleParamsList}" + + " ${protoLogMessage.booleanParamsList}") + } + + fun parse(protoLogInput: InputStream, jsonConfigInput: InputStream, ps: PrintStream) { + val jsonReader = JsonReader(BufferedReader(InputStreamReader(jsonConfigInput))) + val config = configParser.parseConfig(jsonReader) + val protoLog = WindowManagerLogFileProto.parseFrom(protoLogInput) + + if (protoLog.magicNumber != magicNumber) { + throw InvalidInputException("ProtoLog file magic number is invalid.") + } + if (protoLog.version != Constants.VERSION) { + throw InvalidInputException("ProtoLog file version not supported by this tool," + + " log version ${protoLog.version}, viewer version ${Constants.VERSION}") + } + + protoLog.logList.forEach { log -> + printTime(log.elapsedRealtimeNanos, protoLog.realTimeToElapsedTimeOffsetMillis, ps) + if (log.messageHash !in config) { + printUnformatted(log, ps, "UNKNOWN") + } else { + val conf = config.getValue(log.messageHash) + try { + printFormatted(log, conf, ps) + } catch (ex: Exception) { + printUnformatted(log, ps, "INVALID") + } + } + } + } +} diff --git a/tools/protologtool/src/com/android/protologtool/ProtoLogCallProcessor.kt b/tools/protologtool/src/com/android/protologtool/ProtoLogCallProcessor.kt new file mode 100644 index 000000000000..29d8ae5c6694 --- /dev/null +++ b/tools/protologtool/src/com/android/protologtool/ProtoLogCallProcessor.kt @@ -0,0 +1,108 @@ +/* + * 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.protologtool + +import com.github.javaparser.ast.CompilationUnit +import com.github.javaparser.ast.expr.Expression +import com.github.javaparser.ast.expr.FieldAccessExpr +import com.github.javaparser.ast.expr.MethodCallExpr +import com.github.javaparser.ast.expr.NameExpr + +/** + * Helper class for visiting all ProtoLog calls. + * For every valid call in the given {@code CompilationUnit} a {@code ProtoLogCallVisitor} callback + * is executed. + */ +open class ProtoLogCallProcessor( + private val protoLogClassName: String, + private val protoLogGroupClassName: String, + private val groupMap: Map<String, LogGroup> +) { + private val protoLogSimpleClassName = protoLogClassName.substringAfterLast('.') + private val protoLogGroupSimpleClassName = protoLogGroupClassName.substringAfterLast('.') + + private fun getLogGroupName( + expr: Expression, + isClassImported: Boolean, + staticImports: Set<String> + ): String { + return when (expr) { + is NameExpr -> when { + expr.nameAsString in staticImports -> expr.nameAsString + else -> + throw InvalidProtoLogCallException("Unknown/not imported ProtoLogGroup", expr) + } + is FieldAccessExpr -> when { + expr.scope.toString() == protoLogGroupClassName + || isClassImported && + expr.scope.toString() == protoLogGroupSimpleClassName -> expr.nameAsString + else -> + throw InvalidProtoLogCallException("Unknown/not imported ProtoLogGroup", expr) + } + else -> throw InvalidProtoLogCallException("Invalid group argument " + + "- must be ProtoLogGroup enum member reference", expr) + } + } + + private fun isProtoCall( + call: MethodCallExpr, + isLogClassImported: Boolean, + staticLogImports: Collection<String> + ): Boolean { + return call.scope.isPresent && call.scope.get().toString() == protoLogClassName || + isLogClassImported && call.scope.isPresent && + call.scope.get().toString() == protoLogSimpleClassName || + !call.scope.isPresent && staticLogImports.contains(call.name.toString()) + } + + open fun process(code: CompilationUnit, callVisitor: ProtoLogCallVisitor?): CompilationUnit { + if (CodeUtils.isWildcardStaticImported(code, protoLogClassName) || + CodeUtils.isWildcardStaticImported(code, protoLogGroupClassName)) { + throw IllegalImportException("Wildcard static imports of $protoLogClassName " + + "and $protoLogGroupClassName methods are not supported.") + } + + val isLogClassImported = CodeUtils.isClassImportedOrSamePackage(code, protoLogClassName) + val staticLogImports = CodeUtils.staticallyImportedMethods(code, protoLogClassName) + val isGroupClassImported = CodeUtils.isClassImportedOrSamePackage(code, + protoLogGroupClassName) + val staticGroupImports = CodeUtils.staticallyImportedMethods(code, protoLogGroupClassName) + + code.findAll(MethodCallExpr::class.java) + .filter { call -> + isProtoCall(call, isLogClassImported, staticLogImports) + }.forEach { call -> + if (call.arguments.size < 2) { + throw InvalidProtoLogCallException("Method signature does not match " + + "any ProtoLog method.", call) + } + + val messageString = CodeUtils.concatMultilineString(call.getArgument(1)) + val groupNameArg = call.getArgument(0) + val groupName = + getLogGroupName(groupNameArg, isGroupClassImported, staticGroupImports) + if (groupName !in groupMap) { + throw InvalidProtoLogCallException("Unknown group argument " + + "- not a ProtoLogGroup enum member", call) + } + + callVisitor?.processCall(call, messageString, LogLevel.getLevelForMethodName( + call.name.toString(), call), groupMap.getValue(groupName)) + } + return code + } +} diff --git a/tools/protologtool/src/com/android/protologtool/ProtoLogCallVisitor.kt b/tools/protologtool/src/com/android/protologtool/ProtoLogCallVisitor.kt new file mode 100644 index 000000000000..42a75f8cc22f --- /dev/null +++ b/tools/protologtool/src/com/android/protologtool/ProtoLogCallVisitor.kt @@ -0,0 +1,23 @@ +/* + * 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.protologtool + +import com.github.javaparser.ast.expr.MethodCallExpr + +interface ProtoLogCallVisitor { + fun processCall(call: MethodCallExpr, messageString: String, level: LogLevel, group: LogGroup) +} diff --git a/tools/protologtool/src/com/android/protologtool/ProtoLogGroupReader.kt b/tools/protologtool/src/com/android/protologtool/ProtoLogGroupReader.kt new file mode 100644 index 000000000000..664c8a6506b2 --- /dev/null +++ b/tools/protologtool/src/com/android/protologtool/ProtoLogGroupReader.kt @@ -0,0 +1,60 @@ +/* + * 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.protologtool + +import com.android.protologtool.Constants.ENUM_VALUES_METHOD +import com.android.protologtool.Constants.GET_TAG_METHOD +import com.android.protologtool.Constants.IS_ENABLED_METHOD +import com.android.protologtool.Constants.IS_LOG_TO_LOGCAT_METHOD +import java.io.File +import java.lang.RuntimeException +import java.net.URLClassLoader + +class ProtoLogGroupReader { + private fun getClassloaderForJar(jarPath: String): ClassLoader { + val jarFile = File(jarPath) + val url = jarFile.toURI().toURL() + return URLClassLoader(arrayOf(url), ProtoLogGroupReader::class.java.classLoader) + } + + private fun getEnumValues(clazz: Class<*>): List<Enum<*>> { + val valuesMethod = clazz.getMethod(ENUM_VALUES_METHOD) + @Suppress("UNCHECKED_CAST") + return (valuesMethod.invoke(null) as Array<Enum<*>>).toList() + } + + private fun getLogGroupFromEnumValue(group: Any, clazz: Class<*>): LogGroup { + val enabled = clazz.getMethod(IS_ENABLED_METHOD).invoke(group) as Boolean + val textEnabled = clazz.getMethod(IS_LOG_TO_LOGCAT_METHOD).invoke(group) as Boolean + val tag = clazz.getMethod(GET_TAG_METHOD).invoke(group) as String + val name = (group as Enum<*>).name + return LogGroup(name, enabled, textEnabled, tag) + } + + fun loadFromJar(jarPath: String, className: String): Map<String, LogGroup> { + try { + val classLoader = getClassloaderForJar(jarPath) + val clazz = classLoader.loadClass(className) + val values = getEnumValues(clazz) + return values.map { group -> + group.name to getLogGroupFromEnumValue(group, clazz) + }.toMap() + } catch (ex: ReflectiveOperationException) { + throw RuntimeException("Unable to load ProtoLogGroup enum class", ex) + } + } +} diff --git a/tools/protologtool/src/com/android/protologtool/ProtoLogTool.kt b/tools/protologtool/src/com/android/protologtool/ProtoLogTool.kt new file mode 100644 index 000000000000..485a0479cbd9 --- /dev/null +++ b/tools/protologtool/src/com/android/protologtool/ProtoLogTool.kt @@ -0,0 +1,94 @@ +/* + * 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.protologtool + +import com.android.protologtool.CommandOptions.Companion.USAGE +import com.github.javaparser.StaticJavaParser +import java.io.File +import java.io.FileInputStream +import java.io.FileOutputStream +import java.util.jar.JarOutputStream +import java.util.zip.ZipEntry +import kotlin.system.exitProcess + +object ProtoLogTool { + private fun showHelpAndExit() { + println(USAGE) + exitProcess(-1) + } + + private fun processClasses(command: CommandOptions) { + val groups = ProtoLogGroupReader() + .loadFromJar(command.protoLogGroupsJarArg, command.protoLogGroupsClassNameArg) + val out = FileOutputStream(command.outputSourceJarArg) + val outJar = JarOutputStream(out) + val processor = ProtoLogCallProcessor(command.protoLogClassNameArg, + command.protoLogGroupsClassNameArg, groups) + val transformer = SourceTransformer(command.protoLogImplClassNameArg, processor) + + command.javaSourceArgs.forEach { path -> + val file = File(path) + val code = StaticJavaParser.parse(file) + val outSrc = transformer.processClass(code) + val pack = if (code.packageDeclaration.isPresent) code.packageDeclaration + .get().nameAsString else "" + val newPath = pack.replace('.', '/') + '/' + file.name + outJar.putNextEntry(ZipEntry(newPath)) + outJar.write(outSrc.toByteArray()) + outJar.closeEntry() + } + + outJar.close() + out.close() + } + + private fun viewerConf(command: CommandOptions) { + val groups = ProtoLogGroupReader() + .loadFromJar(command.protoLogGroupsJarArg, command.protoLogGroupsClassNameArg) + val processor = ProtoLogCallProcessor(command.protoLogClassNameArg, + command.protoLogGroupsClassNameArg, groups) + val builder = ViewerConfigBuilder(processor) + command.javaSourceArgs.forEach { path -> + val file = File(path) + builder.processClass(StaticJavaParser.parse(file)) + } + val out = FileOutputStream(command.viewerConfigJsonArg) + out.write(builder.build().toByteArray()) + out.close() + } + + fun read(command: CommandOptions) { + LogParser(ViewerConfigParser()) + .parse(FileInputStream(command.logProtofileArg), + FileInputStream(command.viewerConfigJsonArg), System.out) + } + + @JvmStatic + fun main(args: Array<String>) { + try { + val command = CommandOptions(args) + when (command.command) { + CommandOptions.TRANSFORM_CALLS_CMD -> processClasses(command) + CommandOptions.GENERATE_CONFIG_CMD -> viewerConf(command) + CommandOptions.READ_LOG_CMD -> read(command) + } + } catch (ex: InvalidCommandException) { + println(ex.message) + showHelpAndExit() + } + } +} diff --git a/tools/protologtool/src/com/android/protologtool/SourceTransformer.kt b/tools/protologtool/src/com/android/protologtool/SourceTransformer.kt new file mode 100644 index 000000000000..319a8170dca8 --- /dev/null +++ b/tools/protologtool/src/com/android/protologtool/SourceTransformer.kt @@ -0,0 +1,162 @@ +/* + * 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.protologtool + +import com.android.protologtool.Constants.IS_LOG_TO_ANY_METHOD +import com.github.javaparser.StaticJavaParser +import com.github.javaparser.ast.CompilationUnit +import com.github.javaparser.ast.NodeList +import com.github.javaparser.ast.body.VariableDeclarator +import com.github.javaparser.ast.expr.BooleanLiteralExpr +import com.github.javaparser.ast.expr.CastExpr +import com.github.javaparser.ast.expr.FieldAccessExpr +import com.github.javaparser.ast.expr.IntegerLiteralExpr +import com.github.javaparser.ast.expr.MethodCallExpr +import com.github.javaparser.ast.expr.NameExpr +import com.github.javaparser.ast.expr.NullLiteralExpr +import com.github.javaparser.ast.expr.SimpleName +import com.github.javaparser.ast.expr.VariableDeclarationExpr +import com.github.javaparser.ast.stmt.BlockStmt +import com.github.javaparser.ast.stmt.ExpressionStmt +import com.github.javaparser.ast.stmt.IfStmt +import com.github.javaparser.ast.type.ArrayType +import com.github.javaparser.printer.PrettyPrinter +import com.github.javaparser.printer.PrettyPrinterConfiguration +import com.github.javaparser.printer.lexicalpreservation.LexicalPreservingPrinter + +class SourceTransformer( + protoLogImplClassName: String, + private val protoLogCallProcessor: ProtoLogCallProcessor +) : ProtoLogCallVisitor { + override fun processCall( + call: MethodCallExpr, + messageString: String, + level: LogLevel, + group: LogGroup + ) { + // Input format: ProtoLog.e(GROUP, "msg %d", arg) + if (!call.parentNode.isPresent) { + // Should never happen + throw RuntimeException("Unable to process log call $call " + + "- no parent node in AST") + } + if (call.parentNode.get() !is ExpressionStmt) { + // Should never happen + throw RuntimeException("Unable to process log call $call " + + "- parent node in AST is not an ExpressionStmt") + } + val parentStmt = call.parentNode.get() as ExpressionStmt + if (!parentStmt.parentNode.isPresent) { + // Should never happen + throw RuntimeException("Unable to process log call $call " + + "- no grandparent node in AST") + } + val ifStmt: IfStmt + if (group.enabled) { + val hash = CodeUtils.hash(messageString, level) + val newCall = call.clone() + if (!group.textEnabled) { + // Remove message string if text logging is not enabled by default. + // Out: ProtoLog.e(GROUP, null, arg) + newCall.arguments[1].replace(NameExpr("null")) + } + // Insert message string hash as a second argument. + // Out: ProtoLog.e(GROUP, 1234, null, arg) + newCall.arguments.add(1, IntegerLiteralExpr(hash)) + val argTypes = CodeUtils.parseFormatString(messageString) + val typeMask = CodeUtils.logDataTypesToBitMask(argTypes) + // Insert bitmap representing which Number parameters are to be considered as + // floating point numbers. + // Out: ProtoLog.e(GROUP, 1234, 0, null, arg) + newCall.arguments.add(2, IntegerLiteralExpr(typeMask)) + // Replace call to a stub method with an actual implementation. + // Out: com.android.server.wm.ProtoLogImpl.e(GROUP, 1234, null, arg) + newCall.setScope(protoLogImplClassNode) + // Create a call to GROUP.isLogAny() + // Out: GROUP.isLogAny() + val isLogAnyExpr = MethodCallExpr(newCall.arguments[0].clone(), + SimpleName(IS_LOG_TO_ANY_METHOD)) + if (argTypes.size != call.arguments.size - 2) { + throw InvalidProtoLogCallException( + "Number of arguments does not mach format string", call) + } + val blockStmt = BlockStmt() + if (argTypes.isNotEmpty()) { + // Assign every argument to a variable to check its type in compile time + // (this is assignment is optimized-out by dex tool, there is no runtime impact)/ + // Out: long protoLogParam0 = arg + argTypes.forEachIndexed { idx, type -> + val varName = "protoLogParam$idx" + val declaration = VariableDeclarator(type.type, varName, + type.toType(newCall.arguments[idx + 4].clone())) + blockStmt.addStatement(ExpressionStmt(VariableDeclarationExpr(declaration))) + newCall.setArgument(idx + 4, NameExpr(SimpleName(varName))) + } + } else { + // Assign (Object[])null as the vararg parameter to prevent allocating an empty + // object array. + val nullArray = CastExpr(ArrayType(objectType), NullLiteralExpr()) + newCall.addArgument(nullArray) + } + blockStmt.addStatement(ExpressionStmt(newCall)) + // Create an IF-statement with the previously created condition. + // Out: if (GROUP.isLogAny()) { + // long protoLogParam0 = arg; + // com.android.server.wm.ProtoLogImpl.e(GROUP, 1234, 0, null, protoLogParam0); + // } + ifStmt = IfStmt(isLogAnyExpr, blockStmt, null) + } else { + // Surround with if (false). + val newCall = parentStmt.clone() + ifStmt = IfStmt(BooleanLiteralExpr(false), BlockStmt(NodeList(newCall)), null) + newCall.setBlockComment(" ${group.name} is disabled ") + } + // Inline the new statement. + val printedIfStmt = inlinePrinter.print(ifStmt) + // Append blank lines to preserve line numbering in file (to allow debugging) + val newLines = LexicalPreservingPrinter.print(parentStmt).count { c -> c == '\n' } + val newStmt = printedIfStmt.substringBeforeLast('}') + ("\n".repeat(newLines)) + '}' + val inlinedIfStmt = StaticJavaParser.parseStatement(newStmt) + LexicalPreservingPrinter.setup(inlinedIfStmt) + // Replace the original call. + if (!parentStmt.replace(inlinedIfStmt)) { + // Should never happen + throw RuntimeException("Unable to process log call $call " + + "- unable to replace the call.") + } + } + + private val inlinePrinter: PrettyPrinter + private val objectType = StaticJavaParser.parseClassOrInterfaceType("Object") + + init { + val config = PrettyPrinterConfiguration() + config.endOfLineCharacter = " " + config.indentSize = 0 + config.tabWidth = 1 + inlinePrinter = PrettyPrinter(config) + } + + private val protoLogImplClassNode = + StaticJavaParser.parseExpression<FieldAccessExpr>(protoLogImplClassName) + + fun processClass(compilationUnit: CompilationUnit): String { + LexicalPreservingPrinter.setup(compilationUnit) + protoLogCallProcessor.process(compilationUnit, this) + return LexicalPreservingPrinter.print(compilationUnit) + } +} diff --git a/tools/protologtool/src/com/android/protologtool/ViewerConfigBuilder.kt b/tools/protologtool/src/com/android/protologtool/ViewerConfigBuilder.kt new file mode 100644 index 000000000000..8ce9a49c0302 --- /dev/null +++ b/tools/protologtool/src/com/android/protologtool/ViewerConfigBuilder.kt @@ -0,0 +1,91 @@ +/* + * 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.protologtool + +import com.android.json.stream.JsonWriter +import com.github.javaparser.ast.CompilationUnit +import com.android.protologtool.Constants.VERSION +import com.github.javaparser.ast.expr.MethodCallExpr +import java.io.StringWriter + +class ViewerConfigBuilder( + private val protoLogCallVisitor: ProtoLogCallProcessor +) : ProtoLogCallVisitor { + override fun processCall( + call: MethodCallExpr, + messageString: String, + level: LogLevel, + group: LogGroup + ) { + if (group.enabled) { + val key = CodeUtils.hash(messageString, level) + if (statements.containsKey(key)) { + if (statements[key] != Triple(messageString, level, group)) { + throw HashCollisionException( + "Please modify the log message \"$messageString\" " + + "or \"${statements[key]}\" - their hashes are equal.") + } + } else { + groups.add(group) + statements[key] = Triple(messageString, level, group) + } + } + } + + private val statements: MutableMap<Int, Triple<String, LogLevel, LogGroup>> = mutableMapOf() + private val groups: MutableSet<LogGroup> = mutableSetOf() + + fun processClass(unit: CompilationUnit) { + protoLogCallVisitor.process(unit, this) + } + + fun build(): String { + val stringWriter = StringWriter() + val writer = JsonWriter(stringWriter) + writer.setIndent(" ") + writer.beginObject() + writer.name("version") + writer.value(VERSION) + writer.name("messages") + writer.beginObject() + statements.toSortedMap().forEach { (key, value) -> + writer.name(key.toString()) + writer.beginObject() + writer.name("message") + writer.value(value.first) + writer.name("level") + writer.value(value.second.name) + writer.name("group") + writer.value(value.third.name) + writer.endObject() + } + writer.endObject() + writer.name("groups") + writer.beginObject() + groups.toSortedSet(Comparator { o1, o2 -> o1.name.compareTo(o2.name) }).forEach { group -> + writer.name(group.name) + writer.beginObject() + writer.name("tag") + writer.value(group.tag) + writer.endObject() + } + writer.endObject() + writer.endObject() + stringWriter.buffer.append('\n') + return stringWriter.toString() + } +} diff --git a/tools/protologtool/src/com/android/protologtool/ViewerConfigParser.kt b/tools/protologtool/src/com/android/protologtool/ViewerConfigParser.kt new file mode 100644 index 000000000000..69cf92d4d228 --- /dev/null +++ b/tools/protologtool/src/com/android/protologtool/ViewerConfigParser.kt @@ -0,0 +1,125 @@ +/* + * 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.protologtool + +import com.android.json.stream.JsonReader + +open class ViewerConfigParser { + data class MessageEntry( + val messageString: String, + val level: String, + val groupName: String + ) + + fun parseMessage(jsonReader: JsonReader): MessageEntry { + jsonReader.beginObject() + var message: String? = null + var level: String? = null + var groupName: String? = null + while (jsonReader.hasNext()) { + val key = jsonReader.nextName() + when (key) { + "message" -> message = jsonReader.nextString() + "level" -> level = jsonReader.nextString() + "group" -> groupName = jsonReader.nextString() + else -> jsonReader.skipValue() + } + } + jsonReader.endObject() + if (message.isNullOrBlank() || level.isNullOrBlank() || groupName.isNullOrBlank()) { + throw InvalidViewerConfigException("Invalid message entry in viewer config") + } + return MessageEntry(message, level, groupName) + } + + data class GroupEntry(val tag: String) + + fun parseGroup(jsonReader: JsonReader): GroupEntry { + jsonReader.beginObject() + var tag: String? = null + while (jsonReader.hasNext()) { + val key = jsonReader.nextName() + when (key) { + "tag" -> tag = jsonReader.nextString() + else -> jsonReader.skipValue() + } + } + jsonReader.endObject() + if (tag.isNullOrBlank()) { + throw InvalidViewerConfigException("Invalid group entry in viewer config") + } + return GroupEntry(tag) + } + + fun parseMessages(jsonReader: JsonReader): Map<Int, MessageEntry> { + val config: MutableMap<Int, MessageEntry> = mutableMapOf() + jsonReader.beginObject() + while (jsonReader.hasNext()) { + val key = jsonReader.nextName() + val hash = key.toIntOrNull() + ?: throw InvalidViewerConfigException("Invalid key in messages viewer config") + config[hash] = parseMessage(jsonReader) + } + jsonReader.endObject() + return config + } + + fun parseGroups(jsonReader: JsonReader): Map<String, GroupEntry> { + val config: MutableMap<String, GroupEntry> = mutableMapOf() + jsonReader.beginObject() + while (jsonReader.hasNext()) { + val key = jsonReader.nextName() + config[key] = parseGroup(jsonReader) + } + jsonReader.endObject() + return config + } + + data class ConfigEntry(val messageString: String, val level: String, val tag: String) + + open fun parseConfig(jsonReader: JsonReader): Map<Int, ConfigEntry> { + var messages: Map<Int, MessageEntry>? = null + var groups: Map<String, GroupEntry>? = null + var version: String? = null + + jsonReader.beginObject() + while (jsonReader.hasNext()) { + val key = jsonReader.nextName() + when (key) { + "messages" -> messages = parseMessages(jsonReader) + "groups" -> groups = parseGroups(jsonReader) + "version" -> version = jsonReader.nextString() + + else -> jsonReader.skipValue() + } + } + jsonReader.endObject() + if (messages == null || groups == null || version == null) { + throw InvalidViewerConfigException("Invalid config - definitions missing") + } + if (version != Constants.VERSION) { + throw InvalidViewerConfigException("Viewer config version not supported by this tool," + + " config version $version, viewer version ${Constants.VERSION}") + } + return messages.map { msg -> + msg.key to ConfigEntry( + msg.value.messageString, msg.value.level, groups[msg.value.groupName]?.tag + ?: throw InvalidViewerConfigException( + "Group definition missing for ${msg.value.groupName}")) + }.toMap() + } +} diff --git a/tools/protologtool/src/com/android/protologtool/exceptions.kt b/tools/protologtool/src/com/android/protologtool/exceptions.kt new file mode 100644 index 000000000000..2199785a335b --- /dev/null +++ b/tools/protologtool/src/com/android/protologtool/exceptions.kt @@ -0,0 +1,44 @@ +/* + * 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.protologtool + +import com.github.javaparser.ast.Node +import java.lang.Exception +import java.lang.RuntimeException + +class HashCollisionException(message: String) : RuntimeException(message) + +class IllegalImportException(message: String) : Exception(message) + +class InvalidProtoLogCallException(message: String, node: Node) + : RuntimeException("$message\nAt: $node") + +class InvalidViewerConfigException : Exception { + constructor(message: String) : super(message) + + constructor(message: String, ex: Exception) : super(message, ex) +} + +class InvalidFormatStringException : Exception { + constructor(message: String) : super(message) + + constructor(message: String, ex: Exception) : super(message, ex) +} + +class InvalidInputException(message: String) : Exception(message) + +class InvalidCommandException(message: String) : Exception(message) diff --git a/tools/protologtool/tests/com/android/protologtool/CodeUtilsTest.kt b/tools/protologtool/tests/com/android/protologtool/CodeUtilsTest.kt new file mode 100644 index 000000000000..82daa736e1bc --- /dev/null +++ b/tools/protologtool/tests/com/android/protologtool/CodeUtilsTest.kt @@ -0,0 +1,206 @@ +/* + * 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.protologtool + +import com.github.javaparser.StaticJavaParser +import com.github.javaparser.ast.expr.BinaryExpr +import com.github.javaparser.ast.expr.StringLiteralExpr +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test + +class CodeUtilsTest { + @Test + fun hash() { + assertEquals(-1704685243, CodeUtils.hash("test", LogLevel.DEBUG)) + } + + @Test + fun hash_changeLevel() { + assertEquals(-1176900998, CodeUtils.hash("test", LogLevel.ERROR)) + } + + @Test + fun hash_changeMessage() { + assertEquals(-1305634931, CodeUtils.hash("test2", LogLevel.DEBUG)) + } + + @Test + fun isWildcardStaticImported_true() { + val code = """package org.example.test; + import static org.example.Test.*; + """ + assertTrue(CodeUtils.isWildcardStaticImported( + StaticJavaParser.parse(code), "org.example.Test")) + } + + @Test + fun isWildcardStaticImported_notStatic() { + val code = """package org.example.test; + import org.example.Test.*; + """ + assertFalse(CodeUtils.isWildcardStaticImported( + StaticJavaParser.parse(code), "org.example.Test")) + } + + @Test + fun isWildcardStaticImported_differentClass() { + val code = """package org.example.test; + import static org.example.Test2.*; + """ + assertFalse(CodeUtils.isWildcardStaticImported( + StaticJavaParser.parse(code), "org.example.Test")) + } + + @Test + fun isWildcardStaticImported_notWildcard() { + val code = """package org.example.test; + import org.example.Test.test; + """ + assertFalse(CodeUtils.isWildcardStaticImported( + StaticJavaParser.parse(code), "org.example.Test")) + } + + @Test + fun isClassImportedOrSamePackage_imported() { + val code = """package org.example.test; + import org.example.Test; + """ + assertTrue(CodeUtils.isClassImportedOrSamePackage( + StaticJavaParser.parse(code), "org.example.Test")) + } + + @Test + fun isClassImportedOrSamePackage_samePackage() { + val code = """package org.example.test; + """ + assertTrue(CodeUtils.isClassImportedOrSamePackage( + StaticJavaParser.parse(code), "org.example.test.Test")) + } + + @Test + fun isClassImportedOrSamePackage_false() { + val code = """package org.example.test; + import org.example.Test; + """ + assertFalse(CodeUtils.isClassImportedOrSamePackage( + StaticJavaParser.parse(code), "org.example.Test2")) + } + + @Test + fun staticallyImportedMethods_ab() { + val code = """ + import static org.example.Test.a; + import static org.example.Test.b; + """ + val imported = CodeUtils.staticallyImportedMethods(StaticJavaParser.parse(code), + "org.example.Test") + assertTrue(imported.containsAll(listOf("a", "b"))) + assertEquals(2, imported.size) + } + + @Test + fun staticallyImportedMethods_differentClass() { + val code = """ + import static org.example.Test.a; + import static org.example.Test2.b; + """ + val imported = CodeUtils.staticallyImportedMethods(StaticJavaParser.parse(code), + "org.example.Test") + assertTrue(imported.containsAll(listOf("a"))) + assertEquals(1, imported.size) + } + + @Test + fun staticallyImportedMethods_notStatic() { + val code = """ + import static org.example.Test.a; + import org.example.Test.b; + """ + val imported = CodeUtils.staticallyImportedMethods(StaticJavaParser.parse(code), + "org.example.Test") + assertTrue(imported.containsAll(listOf("a"))) + assertEquals(1, imported.size) + } + + @Test + fun concatMultilineString_single() { + val str = StringLiteralExpr("test") + val out = CodeUtils.concatMultilineString(str) + assertEquals("test", out) + } + + @Test + fun concatMultilineString_double() { + val str = """ + "test" + "abc" + """ + val code = StaticJavaParser.parseExpression<BinaryExpr>(str) + val out = CodeUtils.concatMultilineString(code) + assertEquals("testabc", out) + } + + @Test + fun concatMultilineString_multiple() { + val str = """ + "test" + "abc" + "1234" + "test" + """ + val code = StaticJavaParser.parseExpression<BinaryExpr>(str) + val out = CodeUtils.concatMultilineString(code) + assertEquals("testabc1234test", out) + } + + @Test + fun parseFormatString() { + val str = "%b %d %o %x %f %e %g %s %%" + val out = CodeUtils.parseFormatString(str) + assertEquals(listOf( + CodeUtils.LogDataTypes.BOOLEAN, + CodeUtils.LogDataTypes.LONG, + CodeUtils.LogDataTypes.LONG, + CodeUtils.LogDataTypes.LONG, + CodeUtils.LogDataTypes.DOUBLE, + CodeUtils.LogDataTypes.DOUBLE, + CodeUtils.LogDataTypes.DOUBLE, + CodeUtils.LogDataTypes.STRING + ), out) + } + + @Test(expected = InvalidFormatStringException::class) + fun parseFormatString_invalid() { + val str = "%q" + CodeUtils.parseFormatString(str) + } + + @Test + fun logDataTypesToBitMask() { + val types = listOf(CodeUtils.LogDataTypes.STRING, CodeUtils.LogDataTypes.DOUBLE, + CodeUtils.LogDataTypes.LONG, CodeUtils.LogDataTypes.BOOLEAN) + val mask = CodeUtils.logDataTypesToBitMask(types) + assertEquals(0b11011000, mask) + } + + @Test(expected = InvalidFormatStringException::class) + fun logDataTypesToBitMask_toManyParams() { + val types = mutableListOf<CodeUtils.LogDataTypes>() + for (i in 0..16) { + types.add(CodeUtils.LogDataTypes.STRING) + } + CodeUtils.logDataTypesToBitMask(types) + } +} diff --git a/tools/protologtool/tests/com/android/protologtool/CommandOptionsTest.kt b/tools/protologtool/tests/com/android/protologtool/CommandOptionsTest.kt new file mode 100644 index 000000000000..c1cd473574c2 --- /dev/null +++ b/tools/protologtool/tests/com/android/protologtool/CommandOptionsTest.kt @@ -0,0 +1,250 @@ +/* + * 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.protologtool + +import org.junit.Assert.assertEquals +import org.junit.Test + +class CommandOptionsTest { + companion object { + val TEST_JAVA_SRC = listOf( + "frameworks/base/services/core/java/com/android/server/wm/" + + "AccessibilityController.java", + "frameworks/base/services/core/java/com/android/server/wm/ActivityDisplay.java", + "frameworks/base/services/core/java/com/android/server/wm/" + + "ActivityMetricsLaunchObserver.java" + ) + private const val TEST_PROTOLOG_CLASS = "com.android.server.wm.ProtoLog" + private const val TEST_PROTOLOGIMPL_CLASS = "com.android.server.wm.ProtoLogImpl" + private const val TEST_PROTOLOGGROUP_CLASS = "com.android.server.wm.ProtoLogGroup" + private const val TEST_PROTOLOGGROUP_JAR = "out/soong/.intermediates/frameworks/base/" + + "services/core/services.core.wm.protologgroups/android_common/javac/" + + "services.core.wm.protologgroups.jar" + private const val TEST_SRC_JAR = "out/soong/.temp/sbox175955373/" + + "services.core.wm.protolog.srcjar" + private const val TEST_VIEWER_JSON = "out/soong/.temp/sbox175955373/" + + "services.core.wm.protolog.json" + private const val TEST_LOG = "./test_log.pb" + } + + @Test(expected = InvalidCommandException::class) + fun noCommand() { + CommandOptions(arrayOf()) + } + + @Test(expected = InvalidCommandException::class) + fun invalidCommand() { + val testLine = "invalid" + CommandOptions(testLine.split(' ').toTypedArray()) + } + + @Test + fun transformClasses() { + val testLine = "transform-protolog-calls --protolog-class $TEST_PROTOLOG_CLASS " + + "--protolog-impl-class $TEST_PROTOLOGIMPL_CLASS " + + "--loggroups-class $TEST_PROTOLOGGROUP_CLASS " + + "--loggroups-jar $TEST_PROTOLOGGROUP_JAR " + + "--output-srcjar $TEST_SRC_JAR ${TEST_JAVA_SRC.joinToString(" ")}" + val cmd = CommandOptions(testLine.split(' ').toTypedArray()) + assertEquals(CommandOptions.TRANSFORM_CALLS_CMD, cmd.command) + assertEquals(TEST_PROTOLOG_CLASS, cmd.protoLogClassNameArg) + assertEquals(TEST_PROTOLOGIMPL_CLASS, cmd.protoLogImplClassNameArg) + assertEquals(TEST_PROTOLOGGROUP_CLASS, cmd.protoLogGroupsClassNameArg) + assertEquals(TEST_PROTOLOGGROUP_JAR, cmd.protoLogGroupsJarArg) + assertEquals(TEST_SRC_JAR, cmd.outputSourceJarArg) + assertEquals(TEST_JAVA_SRC, cmd.javaSourceArgs) + } + + @Test(expected = InvalidCommandException::class) + fun transformClasses_noProtoLogClass() { + val testLine = "transform-protolog-calls " + + "--protolog-impl-class $TEST_PROTOLOGIMPL_CLASS " + + "--loggroups-class $TEST_PROTOLOGGROUP_CLASS " + + "--loggroups-jar $TEST_PROTOLOGGROUP_JAR " + + "--output-srcjar $TEST_SRC_JAR ${TEST_JAVA_SRC.joinToString(" ")}" + CommandOptions(testLine.split(' ').toTypedArray()) + } + + @Test(expected = InvalidCommandException::class) + fun transformClasses_noProtoLogImplClass() { + val testLine = "transform-protolog-calls --protolog-class $TEST_PROTOLOG_CLASS " + + "--loggroups-class $TEST_PROTOLOGGROUP_CLASS " + + "--loggroups-jar $TEST_PROTOLOGGROUP_JAR " + + "--output-srcjar $TEST_SRC_JAR ${TEST_JAVA_SRC.joinToString(" ")}" + CommandOptions(testLine.split(' ').toTypedArray()) + } + + @Test(expected = InvalidCommandException::class) + fun transformClasses_noProtoLogGroupClass() { + val testLine = "transform-protolog-calls --protolog-class $TEST_PROTOLOG_CLASS " + + "--protolog-impl-class $TEST_PROTOLOGIMPL_CLASS " + + "--loggroups-jar $TEST_PROTOLOGGROUP_JAR " + + "--output-srcjar $TEST_SRC_JAR ${TEST_JAVA_SRC.joinToString(" ")}" + CommandOptions(testLine.split(' ').toTypedArray()) + } + + @Test(expected = InvalidCommandException::class) + fun transformClasses_noProtoLogGroupJar() { + val testLine = "transform-protolog-calls --protolog-class $TEST_PROTOLOG_CLASS " + + "--protolog-impl-class $TEST_PROTOLOGIMPL_CLASS " + + "--loggroups-class $TEST_PROTOLOGGROUP_CLASS " + + "--output-srcjar $TEST_SRC_JAR ${TEST_JAVA_SRC.joinToString(" ")}" + CommandOptions(testLine.split(' ').toTypedArray()) + } + + @Test(expected = InvalidCommandException::class) + fun transformClasses_noOutJar() { + val testLine = "transform-protolog-calls --protolog-class $TEST_PROTOLOG_CLASS " + + "--protolog-impl-class $TEST_PROTOLOGIMPL_CLASS " + + "--loggroups-class $TEST_PROTOLOGGROUP_CLASS " + + "--loggroups-jar $TEST_PROTOLOGGROUP_JAR " + + TEST_JAVA_SRC.joinToString(" ") + CommandOptions(testLine.split(' ').toTypedArray()) + } + + @Test(expected = InvalidCommandException::class) + fun transformClasses_noJavaInput() { + val testLine = "transform-protolog-calls --protolog-class $TEST_PROTOLOG_CLASS " + + "--protolog-impl-class $TEST_PROTOLOGIMPL_CLASS " + + "--loggroups-class $TEST_PROTOLOGGROUP_CLASS " + + "--loggroups-jar $TEST_PROTOLOGGROUP_JAR " + + "--output-srcjar $TEST_SRC_JAR" + CommandOptions(testLine.split(' ').toTypedArray()) + } + + @Test(expected = InvalidCommandException::class) + fun transformClasses_invalidProtoLogClass() { + val testLine = "transform-protolog-calls --protolog-class invalid " + + "--protolog-impl-class $TEST_PROTOLOGIMPL_CLASS " + + "--loggroups-class $TEST_PROTOLOGGROUP_CLASS " + + "--loggroups-jar $TEST_PROTOLOGGROUP_JAR " + + "--output-srcjar $TEST_SRC_JAR ${TEST_JAVA_SRC.joinToString(" ")}" + CommandOptions(testLine.split(' ').toTypedArray()) + } + + @Test(expected = InvalidCommandException::class) + fun transformClasses_invalidProtoLogImplClass() { + val testLine = "transform-protolog-calls --protolog-class $TEST_PROTOLOG_CLASS " + + "--protolog-impl-class invalid " + + "--loggroups-class $TEST_PROTOLOGGROUP_CLASS " + + "--loggroups-jar $TEST_PROTOLOGGROUP_JAR " + + "--output-srcjar $TEST_SRC_JAR ${TEST_JAVA_SRC.joinToString(" ")}" + CommandOptions(testLine.split(' ').toTypedArray()) + } + + @Test(expected = InvalidCommandException::class) + fun transformClasses_invalidProtoLogGroupClass() { + val testLine = "transform-protolog-calls --protolog-class $TEST_PROTOLOG_CLASS " + + "--protolog-impl-class $TEST_PROTOLOGIMPL_CLASS " + + "--loggroups-class invalid " + + "--loggroups-jar $TEST_PROTOLOGGROUP_JAR " + + "--output-srcjar $TEST_SRC_JAR ${TEST_JAVA_SRC.joinToString(" ")}" + CommandOptions(testLine.split(' ').toTypedArray()) + } + + @Test(expected = InvalidCommandException::class) + fun transformClasses_invalidProtoLogGroupJar() { + val testLine = "transform-protolog-calls --protolog-class $TEST_PROTOLOG_CLASS " + + "--protolog-impl-class $TEST_PROTOLOGIMPL_CLASS " + + "--loggroups-class $TEST_PROTOLOGGROUP_CLASS " + + "--loggroups-jar invalid.txt " + + "--output-srcjar $TEST_SRC_JAR ${TEST_JAVA_SRC.joinToString(" ")}" + CommandOptions(testLine.split(' ').toTypedArray()) + } + + @Test(expected = InvalidCommandException::class) + fun transformClasses_invalidOutJar() { + val testLine = "transform-protolog-calls --protolog-class $TEST_PROTOLOG_CLASS " + + "--protolog-impl-class $TEST_PROTOLOGIMPL_CLASS " + + "--loggroups-class $TEST_PROTOLOGGROUP_CLASS " + + "--loggroups-jar $TEST_PROTOLOGGROUP_JAR " + + "--output-srcjar invalid.db ${TEST_JAVA_SRC.joinToString(" ")}" + CommandOptions(testLine.split(' ').toTypedArray()) + } + + @Test(expected = InvalidCommandException::class) + fun transformClasses_invalidJavaInput() { + val testLine = "transform-protolog-calls --protolog-class $TEST_PROTOLOG_CLASS " + + "--protolog-impl-class $TEST_PROTOLOGIMPL_CLASS " + + "--loggroups-class $TEST_PROTOLOGGROUP_CLASS " + + "--loggroups-jar $TEST_PROTOLOGGROUP_JAR " + + "--output-srcjar $TEST_SRC_JAR invalid.py" + CommandOptions(testLine.split(' ').toTypedArray()) + } + + @Test(expected = InvalidCommandException::class) + fun transformClasses_unknownParam() { + val testLine = "transform-protolog-calls --protolog-class $TEST_PROTOLOG_CLASS " + + "--unknown test --protolog-impl-class $TEST_PROTOLOGIMPL_CLASS " + + "--loggroups-class $TEST_PROTOLOGGROUP_CLASS " + + "--loggroups-jar $TEST_PROTOLOGGROUP_JAR " + + "--output-srcjar $TEST_SRC_JAR ${TEST_JAVA_SRC.joinToString(" ")}" + CommandOptions(testLine.split(' ').toTypedArray()) + } + + @Test(expected = InvalidCommandException::class) + fun transformClasses_noValue() { + val testLine = "transform-protolog-calls --protolog-class $TEST_PROTOLOG_CLASS " + + "--protolog-impl-class " + + "--loggroups-class $TEST_PROTOLOGGROUP_CLASS " + + "--loggroups-jar $TEST_PROTOLOGGROUP_JAR " + + "--output-srcjar $TEST_SRC_JAR ${TEST_JAVA_SRC.joinToString(" ")}" + CommandOptions(testLine.split(' ').toTypedArray()) + } + + @Test + fun generateConfig() { + val testLine = "generate-viewer-config --protolog-class $TEST_PROTOLOG_CLASS " + + "--loggroups-class $TEST_PROTOLOGGROUP_CLASS " + + "--loggroups-jar $TEST_PROTOLOGGROUP_JAR " + + "--viewer-conf $TEST_VIEWER_JSON ${TEST_JAVA_SRC.joinToString(" ")}" + val cmd = CommandOptions(testLine.split(' ').toTypedArray()) + assertEquals(CommandOptions.GENERATE_CONFIG_CMD, cmd.command) + assertEquals(TEST_PROTOLOG_CLASS, cmd.protoLogClassNameArg) + assertEquals(TEST_PROTOLOGGROUP_CLASS, cmd.protoLogGroupsClassNameArg) + assertEquals(TEST_PROTOLOGGROUP_JAR, cmd.protoLogGroupsJarArg) + assertEquals(TEST_VIEWER_JSON, cmd.viewerConfigJsonArg) + assertEquals(TEST_JAVA_SRC, cmd.javaSourceArgs) + } + + @Test(expected = InvalidCommandException::class) + fun generateConfig_noViewerConfig() { + val testLine = "generate-viewer-config --protolog-class $TEST_PROTOLOG_CLASS " + + "--loggroups-class $TEST_PROTOLOGGROUP_CLASS " + + "--loggroups-jar $TEST_PROTOLOGGROUP_JAR " + + TEST_JAVA_SRC.joinToString(" ") + CommandOptions(testLine.split(' ').toTypedArray()) + } + + @Test(expected = InvalidCommandException::class) + fun generateConfig_invalidViewerConfig() { + val testLine = "generate-viewer-config --protolog-class $TEST_PROTOLOG_CLASS " + + "--loggroups-class $TEST_PROTOLOGGROUP_CLASS " + + "--loggroups-jar $TEST_PROTOLOGGROUP_JAR " + + "--viewer-conf invalid.yaml ${TEST_JAVA_SRC.joinToString(" ")}" + CommandOptions(testLine.split(' ').toTypedArray()) + } + + @Test + fun readLog() { + val testLine = "read-log --viewer-conf $TEST_VIEWER_JSON $TEST_LOG" + val cmd = CommandOptions(testLine.split(' ').toTypedArray()) + assertEquals(CommandOptions.READ_LOG_CMD, cmd.command) + assertEquals(TEST_VIEWER_JSON, cmd.viewerConfigJsonArg) + assertEquals(TEST_LOG, cmd.logProtofileArg) + } +} diff --git a/tools/protologtool/tests/com/android/protologtool/LogParserTest.kt b/tools/protologtool/tests/com/android/protologtool/LogParserTest.kt new file mode 100644 index 000000000000..7106ea6fa168 --- /dev/null +++ b/tools/protologtool/tests/com/android/protologtool/LogParserTest.kt @@ -0,0 +1,187 @@ +/* + * 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.protologtool + +import com.android.json.stream.JsonReader +import com.android.server.wm.ProtoLogMessage +import com.android.server.wm.WindowManagerLogFileProto +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Test +import org.mockito.Mockito +import org.mockito.Mockito.mock +import java.io.ByteArrayOutputStream +import java.io.InputStream +import java.io.OutputStream +import java.io.PrintStream +import java.text.SimpleDateFormat +import java.util.Date +import java.util.Locale + +class LogParserTest { + private val configParser: ViewerConfigParser = mock(ViewerConfigParser::class.java) + private val parser = LogParser(configParser) + private var config: MutableMap<Int, ViewerConfigParser.ConfigEntry> = mutableMapOf() + private var outStream: OutputStream = ByteArrayOutputStream() + private var printStream: PrintStream = PrintStream(outStream) + private val dateFormat = SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US) + + @Before + fun init() { + Mockito.`when`(configParser.parseConfig(any(JsonReader::class.java))).thenReturn(config) + } + + private fun <T> any(type: Class<T>): T = Mockito.any<T>(type) + + private fun getConfigDummyStream(): InputStream { + return "".byteInputStream() + } + + private fun buildProtoInput(logBuilder: WindowManagerLogFileProto.Builder): InputStream { + logBuilder.setVersion(Constants.VERSION) + logBuilder.magicNumber = + WindowManagerLogFileProto.MagicNumber.MAGIC_NUMBER_H.number.toLong() shl 32 or + WindowManagerLogFileProto.MagicNumber.MAGIC_NUMBER_L.number.toLong() + return logBuilder.build().toByteArray().inputStream() + } + + private fun testDate(timeMS: Long): String { + return dateFormat.format(Date(timeMS)) + } + + @Test + fun parse() { + config[70933285] = ViewerConfigParser.ConfigEntry("Test completed successfully: %b", + "ERROR", "WindowManager") + + val logBuilder = WindowManagerLogFileProto.newBuilder() + val logMessageBuilder = ProtoLogMessage.newBuilder() + logMessageBuilder + .setMessageHash(70933285) + .setElapsedRealtimeNanos(0) + .addBooleanParams(true) + logBuilder.addLog(logMessageBuilder.build()) + + parser.parse(buildProtoInput(logBuilder), getConfigDummyStream(), printStream) + + assertEquals("${testDate(0)} ERROR WindowManager: Test completed successfully: true\n", + outStream.toString()) + } + + @Test + fun parse_formatting() { + config[123] = ViewerConfigParser.ConfigEntry("Test completed successfully: %b %d %% %o" + + " %x %e %g %s %f", "ERROR", "WindowManager") + + val logBuilder = WindowManagerLogFileProto.newBuilder() + val logMessageBuilder = ProtoLogMessage.newBuilder() + logMessageBuilder + .setMessageHash(123) + .setElapsedRealtimeNanos(0) + .addBooleanParams(true) + .addAllSint64Params(listOf(1000, 20000, 300000)) + .addAllDoubleParams(listOf(0.1, 0.00001, 1000.1)) + .addStrParams("test") + logBuilder.addLog(logMessageBuilder.build()) + + parser.parse(buildProtoInput(logBuilder), getConfigDummyStream(), printStream) + + assertEquals("${testDate(0)} ERROR WindowManager: Test completed successfully: " + + "true 1000 % 47040 493e0 1.000000e-01 1.00000e-05 test 1000.100000\n", + outStream.toString()) + } + + @Test + fun parse_invalidParamsTooMany() { + config[123] = ViewerConfigParser.ConfigEntry("Test completed successfully: %b %d %% %o", + "ERROR", "WindowManager") + + val logBuilder = WindowManagerLogFileProto.newBuilder() + val logMessageBuilder = ProtoLogMessage.newBuilder() + logMessageBuilder + .setMessageHash(123) + .setElapsedRealtimeNanos(0) + .addBooleanParams(true) + .addAllSint64Params(listOf(1000, 20000, 300000)) + .addAllDoubleParams(listOf(0.1, 0.00001, 1000.1)) + .addStrParams("test") + logBuilder.addLog(logMessageBuilder.build()) + + parser.parse(buildProtoInput(logBuilder), getConfigDummyStream(), printStream) + + assertEquals("${testDate(0)} INVALID: 123 - [test] [1000, 20000, 300000] " + + "[0.1, 1.0E-5, 1000.1] [true]\n", outStream.toString()) + } + + @Test + fun parse_invalidParamsNotEnough() { + config[123] = ViewerConfigParser.ConfigEntry("Test completed successfully: %b %d %% %o" + + " %x %e %g %s %f", "ERROR", "WindowManager") + + val logBuilder = WindowManagerLogFileProto.newBuilder() + val logMessageBuilder = ProtoLogMessage.newBuilder() + logMessageBuilder + .setMessageHash(123) + .setElapsedRealtimeNanos(0) + .addBooleanParams(true) + .addStrParams("test") + logBuilder.addLog(logMessageBuilder.build()) + + parser.parse(buildProtoInput(logBuilder), getConfigDummyStream(), printStream) + + assertEquals("${testDate(0)} INVALID: 123 - [test] [] [] [true]\n", + outStream.toString()) + } + + @Test(expected = InvalidInputException::class) + fun parse_invalidMagicNumber() { + val logBuilder = WindowManagerLogFileProto.newBuilder() + logBuilder.setVersion(Constants.VERSION) + logBuilder.magicNumber = 0 + val stream = logBuilder.build().toByteArray().inputStream() + + parser.parse(stream, getConfigDummyStream(), printStream) + } + + @Test(expected = InvalidInputException::class) + fun parse_invalidVersion() { + val logBuilder = WindowManagerLogFileProto.newBuilder() + logBuilder.setVersion("invalid") + logBuilder.magicNumber = + WindowManagerLogFileProto.MagicNumber.MAGIC_NUMBER_H.number.toLong() shl 32 or + WindowManagerLogFileProto.MagicNumber.MAGIC_NUMBER_L.number.toLong() + val stream = logBuilder.build().toByteArray().inputStream() + + parser.parse(stream, getConfigDummyStream(), printStream) + } + + @Test + fun parse_noConfig() { + val logBuilder = WindowManagerLogFileProto.newBuilder() + val logMessageBuilder = ProtoLogMessage.newBuilder() + logMessageBuilder + .setMessageHash(70933285) + .setElapsedRealtimeNanos(0) + .addBooleanParams(true) + logBuilder.addLog(logMessageBuilder.build()) + + parser.parse(buildProtoInput(logBuilder), getConfigDummyStream(), printStream) + + assertEquals("${testDate(0)} UNKNOWN: 70933285 - [] [] [] [true]\n", + outStream.toString()) + } +} diff --git a/tools/protologtool/tests/com/android/protologtool/ProtoLogCallProcessorTest.kt b/tools/protologtool/tests/com/android/protologtool/ProtoLogCallProcessorTest.kt new file mode 100644 index 000000000000..dcb1f7fe3366 --- /dev/null +++ b/tools/protologtool/tests/com/android/protologtool/ProtoLogCallProcessorTest.kt @@ -0,0 +1,226 @@ +/* + * 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.protologtool + +import com.github.javaparser.StaticJavaParser +import com.github.javaparser.ast.expr.MethodCallExpr +import org.junit.Assert.assertEquals +import org.junit.Test + +class ProtoLogCallProcessorTest { + private data class LogCall( + val call: MethodCallExpr, + val messageString: String, + val level: LogLevel, + val group: LogGroup + ) + + private val groupMap: MutableMap<String, LogGroup> = mutableMapOf() + private val calls: MutableList<LogCall> = mutableListOf() + private val visitor = ProtoLogCallProcessor("org.example.ProtoLog", "org.example.ProtoLogGroup", + groupMap) + private val processor = object : ProtoLogCallVisitor { + override fun processCall( + call: MethodCallExpr, + messageString: String, + level: LogLevel, + group: LogGroup + ) { + calls.add(LogCall(call, messageString, level, group)) + } + } + + private fun checkCalls() { + assertEquals(1, calls.size) + val c = calls[0] + assertEquals("test %b", c.messageString) + assertEquals(groupMap["TEST"], c.group) + assertEquals(LogLevel.DEBUG, c.level) + } + + @Test + fun process_samePackage() { + val code = """ + package org.example; + + class Test { + void test() { + ProtoLog.d(ProtoLogGroup.TEST, "test %b", true); + ProtoLog.e(ProtoLogGroup.ERROR, "error %d", 1); + } + } + """ + groupMap["TEST"] = LogGroup("TEST", true, false, "WindowManager") + groupMap["ERROR"] = LogGroup("ERROR", true, true, "WindowManagerERROR") + visitor.process(StaticJavaParser.parse(code), processor) + assertEquals(2, calls.size) + var c = calls[0] + assertEquals("test %b", c.messageString) + assertEquals(groupMap["TEST"], c.group) + assertEquals(LogLevel.DEBUG, c.level) + c = calls[1] + assertEquals("error %d", c.messageString) + assertEquals(groupMap["ERROR"], c.group) + assertEquals(LogLevel.ERROR, c.level) + } + + @Test + fun process_imported() { + val code = """ + package org.example2; + + import org.example.ProtoLog; + import org.example.ProtoLogGroup; + + class Test { + void test() { + ProtoLog.d(ProtoLogGroup.TEST, "test %b", true); + } + } + """ + groupMap["TEST"] = LogGroup("TEST", true, true, "WindowManager") + visitor.process(StaticJavaParser.parse(code), processor) + checkCalls() + } + + @Test + fun process_importedStatic() { + val code = """ + package org.example2; + + import static org.example.ProtoLog.d; + import static org.example.ProtoLogGroup.TEST; + + class Test { + void test() { + d(TEST, "test %b", true); + } + } + """ + groupMap["TEST"] = LogGroup("TEST", true, true, "WindowManager") + visitor.process(StaticJavaParser.parse(code), processor) + checkCalls() + } + + @Test(expected = InvalidProtoLogCallException::class) + fun process_groupNotImported() { + val code = """ + package org.example2; + + import org.example.ProtoLog; + + class Test { + void test() { + ProtoLog.d(ProtoLogGroup.TEST, "test %b", true); + } + } + """ + groupMap["TEST"] = LogGroup("TEST", true, true, "WindowManager") + visitor.process(StaticJavaParser.parse(code), processor) + } + + @Test + fun process_protoLogNotImported() { + val code = """ + package org.example2; + + import org.example.ProtoLogGroup; + + class Test { + void test() { + ProtoLog.d(ProtoLogGroup.TEST, "test %b", true); + } + } + """ + groupMap["TEST"] = LogGroup("TEST", true, true, "WindowManager") + visitor.process(StaticJavaParser.parse(code), processor) + assertEquals(0, calls.size) + } + + @Test(expected = InvalidProtoLogCallException::class) + fun process_unknownGroup() { + val code = """ + package org.example; + + class Test { + void test() { + ProtoLog.d(ProtoLogGroup.TEST, "test %b", true); + } + } + """ + visitor.process(StaticJavaParser.parse(code), processor) + } + + @Test(expected = InvalidProtoLogCallException::class) + fun process_staticGroup() { + val code = """ + package org.example; + + class Test { + void test() { + ProtoLog.d(TEST, "test %b", true); + } + } + """ + visitor.process(StaticJavaParser.parse(code), processor) + } + + @Test(expected = InvalidProtoLogCallException::class) + fun process_badGroup() { + val code = """ + package org.example; + + class Test { + void test() { + ProtoLog.d(0, "test %b", true); + } + } + """ + visitor.process(StaticJavaParser.parse(code), processor) + } + + @Test(expected = InvalidProtoLogCallException::class) + fun process_invalidSignature() { + val code = """ + package org.example; + + class Test { + void test() { + ProtoLog.d("test"); + } + } + """ + visitor.process(StaticJavaParser.parse(code), processor) + } + + @Test + fun process_disabled() { + // Disabled groups are also processed. + val code = """ + package org.example; + + class Test { + void test() { + ProtoLog.d(ProtoLogGroup.TEST, "test %b", true); + } + } + """ + groupMap["TEST"] = LogGroup("TEST", false, true, "WindowManager") + visitor.process(StaticJavaParser.parse(code), processor) + checkCalls() + } +} diff --git a/tools/protologtool/tests/com/android/protologtool/SourceTransformerTest.kt b/tools/protologtool/tests/com/android/protologtool/SourceTransformerTest.kt new file mode 100644 index 000000000000..7b8dd9a73fa9 --- /dev/null +++ b/tools/protologtool/tests/com/android/protologtool/SourceTransformerTest.kt @@ -0,0 +1,373 @@ +/* + * 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.protologtool + +import com.github.javaparser.StaticJavaParser +import com.github.javaparser.ast.CompilationUnit +import com.github.javaparser.ast.expr.MethodCallExpr +import com.github.javaparser.ast.stmt.IfStmt +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Test +import org.mockito.Mockito + +class SourceTransformerTest { + companion object { + private const val PROTO_LOG_IMPL_PATH = "org.example.ProtoLogImpl" + private val TEST_CODE = """ + package org.example; + + class Test { + void test() { + ProtoLog.w(TEST_GROUP, "test %d %f", 100, 0.1); + } + } + """.trimIndent() + + private val TEST_CODE_MULTILINE = """ + package org.example; + + class Test { + void test() { + ProtoLog.w(TEST_GROUP, "test %d %f " + + "abc %s\n test", 100, + 0.1, "test"); + } + } + """.trimIndent() + + private val TEST_CODE_NO_PARAMS = """ + package org.example; + + class Test { + void test() { + ProtoLog.w(TEST_GROUP, "test"); + } + } + """.trimIndent() + + /* ktlint-disable max-line-length */ + private val TRANSFORMED_CODE_TEXT_ENABLED = """ + package org.example; + + class Test { + void test() { + if (TEST_GROUP.isLogToAny()) { long protoLogParam0 = 100; double protoLogParam1 = 0.1; org.example.ProtoLogImpl.w(TEST_GROUP, 835524026, 9, "test %d %f", protoLogParam0, protoLogParam1); } + } + } + """.trimIndent() + + private val TRANSFORMED_CODE_MULTILINE_TEXT_ENABLED = """ + package org.example; + + class Test { + void test() { + if (TEST_GROUP.isLogToAny()) { long protoLogParam0 = 100; double protoLogParam1 = 0.1; String protoLogParam2 = String.valueOf("test"); org.example.ProtoLogImpl.w(TEST_GROUP, -986393606, 9, "test %d %f " + "abc %s\n test", protoLogParam0, protoLogParam1, protoLogParam2); + + } + } + } + """.trimIndent() + + private val TRANSFORMED_CODE_NO_PARAMS = """ + package org.example; + + class Test { + void test() { + if (TEST_GROUP.isLogToAny()) { org.example.ProtoLogImpl.w(TEST_GROUP, 1282022424, 0, "test", (Object[]) null); } + } + } + """.trimIndent() + + private val TRANSFORMED_CODE_TEXT_DISABLED = """ + package org.example; + + class Test { + void test() { + if (TEST_GROUP.isLogToAny()) { long protoLogParam0 = 100; double protoLogParam1 = 0.1; org.example.ProtoLogImpl.w(TEST_GROUP, 835524026, 9, null, protoLogParam0, protoLogParam1); } + } + } + """.trimIndent() + + private val TRANSFORMED_CODE_MULTILINE_TEXT_DISABLED = """ + package org.example; + + class Test { + void test() { + if (TEST_GROUP.isLogToAny()) { long protoLogParam0 = 100; double protoLogParam1 = 0.1; String protoLogParam2 = String.valueOf("test"); org.example.ProtoLogImpl.w(TEST_GROUP, -986393606, 9, null, protoLogParam0, protoLogParam1, protoLogParam2); + + } + } + } + """.trimIndent() + + private val TRANSFORMED_CODE_DISABLED = """ + package org.example; + + class Test { + void test() { + if (false) { /* TEST_GROUP is disabled */ ProtoLog.w(TEST_GROUP, "test %d %f", 100, 0.1); } + } + } + """.trimIndent() + + private val TRANSFORMED_CODE_MULTILINE_DISABLED = """ + package org.example; + + class Test { + void test() { + if (false) { /* TEST_GROUP is disabled */ ProtoLog.w(TEST_GROUP, "test %d %f " + "abc %s\n test", 100, 0.1, "test"); + + } + } + } + """.trimIndent() + /* ktlint-enable max-line-length */ + } + + private val processor: ProtoLogCallProcessor = Mockito.mock(ProtoLogCallProcessor::class.java) + private val sourceJarWriter = SourceTransformer("org.example.ProtoLogImpl", processor) + + private fun <T> any(type: Class<T>): T = Mockito.any<T>(type) + + @Test + fun processClass_textEnabled() { + val code = StaticJavaParser.parse(TEST_CODE) + + Mockito.`when`(processor.process(any(CompilationUnit::class.java), + any(ProtoLogCallVisitor::class.java))).thenAnswer { invocation -> + val visitor = invocation.arguments[1] as ProtoLogCallVisitor + + visitor.processCall(code.findAll(MethodCallExpr::class.java)[0], "test %d %f", + LogLevel.WARN, LogGroup("TEST_GROUP", true, true, "WM_TEST")) + + invocation.arguments[0] as CompilationUnit + } + + val out = sourceJarWriter.processClass(code) + + val ifStmts = code.findAll(IfStmt::class.java) + assertEquals(1, ifStmts.size) + val ifStmt = ifStmts[0] + assertEquals("TEST_GROUP.${Constants.IS_LOG_TO_ANY_METHOD}()", + ifStmt.condition.toString()) + assertFalse(ifStmt.elseStmt.isPresent) + assertEquals(3, ifStmt.thenStmt.childNodes.size) + val methodCall = ifStmt.thenStmt.findAll(MethodCallExpr::class.java)[0] as MethodCallExpr + assertEquals(PROTO_LOG_IMPL_PATH, methodCall.scope.get().toString()) + assertEquals("w", methodCall.name.asString()) + assertEquals(6, methodCall.arguments.size) + assertEquals("TEST_GROUP", methodCall.arguments[0].toString()) + assertEquals("835524026", methodCall.arguments[1].toString()) + assertEquals(0b1001.toString(), methodCall.arguments[2].toString()) + assertEquals("\"test %d %f\"", methodCall.arguments[3].toString()) + assertEquals("protoLogParam0", methodCall.arguments[4].toString()) + assertEquals("protoLogParam1", methodCall.arguments[5].toString()) + assertEquals(TRANSFORMED_CODE_TEXT_ENABLED, out) + } + + @Test + fun processClass_textEnabledMultiline() { + val code = StaticJavaParser.parse(TEST_CODE_MULTILINE) + + Mockito.`when`(processor.process(any(CompilationUnit::class.java), + any(ProtoLogCallVisitor::class.java))).thenAnswer { invocation -> + val visitor = invocation.arguments[1] as ProtoLogCallVisitor + + visitor.processCall(code.findAll(MethodCallExpr::class.java)[0], + "test %d %f abc %s\n test", LogLevel.WARN, LogGroup("TEST_GROUP", + true, true, "WM_TEST")) + + invocation.arguments[0] as CompilationUnit + } + + val out = sourceJarWriter.processClass(code) + + val ifStmts = code.findAll(IfStmt::class.java) + assertEquals(1, ifStmts.size) + val ifStmt = ifStmts[0] + assertEquals("TEST_GROUP.${Constants.IS_LOG_TO_ANY_METHOD}()", + ifStmt.condition.toString()) + assertFalse(ifStmt.elseStmt.isPresent) + assertEquals(4, ifStmt.thenStmt.childNodes.size) + val methodCall = ifStmt.thenStmt.findAll(MethodCallExpr::class.java)[1] as MethodCallExpr + assertEquals(PROTO_LOG_IMPL_PATH, methodCall.scope.get().toString()) + assertEquals("w", methodCall.name.asString()) + assertEquals(7, methodCall.arguments.size) + assertEquals("TEST_GROUP", methodCall.arguments[0].toString()) + assertEquals("-986393606", methodCall.arguments[1].toString()) + assertEquals(0b001001.toString(), methodCall.arguments[2].toString()) + assertEquals("protoLogParam0", methodCall.arguments[4].toString()) + assertEquals("protoLogParam1", methodCall.arguments[5].toString()) + assertEquals("protoLogParam2", methodCall.arguments[6].toString()) + assertEquals(TRANSFORMED_CODE_MULTILINE_TEXT_ENABLED, out) + } + + @Test + fun processClass_noParams() { + val code = StaticJavaParser.parse(TEST_CODE_NO_PARAMS) + + Mockito.`when`(processor.process(any(CompilationUnit::class.java), + any(ProtoLogCallVisitor::class.java))).thenAnswer { invocation -> + val visitor = invocation.arguments[1] as ProtoLogCallVisitor + + visitor.processCall(code.findAll(MethodCallExpr::class.java)[0], "test", + LogLevel.WARN, LogGroup("TEST_GROUP", true, true, "WM_TEST")) + + invocation.arguments[0] as CompilationUnit + } + + val out = sourceJarWriter.processClass(code) + + val ifStmts = code.findAll(IfStmt::class.java) + assertEquals(1, ifStmts.size) + val ifStmt = ifStmts[0] + assertEquals("TEST_GROUP.${Constants.IS_LOG_TO_ANY_METHOD}()", + ifStmt.condition.toString()) + assertFalse(ifStmt.elseStmt.isPresent) + assertEquals(1, ifStmt.thenStmt.childNodes.size) + val methodCall = ifStmt.thenStmt.findAll(MethodCallExpr::class.java)[0] as MethodCallExpr + assertEquals(PROTO_LOG_IMPL_PATH, methodCall.scope.get().toString()) + assertEquals("w", methodCall.name.asString()) + assertEquals(5, methodCall.arguments.size) + assertEquals("TEST_GROUP", methodCall.arguments[0].toString()) + assertEquals("1282022424", methodCall.arguments[1].toString()) + assertEquals(0.toString(), methodCall.arguments[2].toString()) + assertEquals(TRANSFORMED_CODE_NO_PARAMS, out) + } + + @Test + fun processClass_textDisabled() { + val code = StaticJavaParser.parse(TEST_CODE) + + Mockito.`when`(processor.process(any(CompilationUnit::class.java), + any(ProtoLogCallVisitor::class.java))).thenAnswer { invocation -> + val visitor = invocation.arguments[1] as ProtoLogCallVisitor + + visitor.processCall(code.findAll(MethodCallExpr::class.java)[0], "test %d %f", + LogLevel.WARN, LogGroup("TEST_GROUP", true, false, "WM_TEST")) + + invocation.arguments[0] as CompilationUnit + } + + val out = sourceJarWriter.processClass(code) + + val ifStmts = code.findAll(IfStmt::class.java) + assertEquals(1, ifStmts.size) + val ifStmt = ifStmts[0] + assertEquals("TEST_GROUP.${Constants.IS_LOG_TO_ANY_METHOD}()", + ifStmt.condition.toString()) + assertFalse(ifStmt.elseStmt.isPresent) + assertEquals(3, ifStmt.thenStmt.childNodes.size) + val methodCall = ifStmt.thenStmt.findAll(MethodCallExpr::class.java)[0] as MethodCallExpr + assertEquals(PROTO_LOG_IMPL_PATH, methodCall.scope.get().toString()) + assertEquals("w", methodCall.name.asString()) + assertEquals(6, methodCall.arguments.size) + assertEquals("TEST_GROUP", methodCall.arguments[0].toString()) + assertEquals("835524026", methodCall.arguments[1].toString()) + assertEquals(0b1001.toString(), methodCall.arguments[2].toString()) + assertEquals("null", methodCall.arguments[3].toString()) + assertEquals("protoLogParam0", methodCall.arguments[4].toString()) + assertEquals("protoLogParam1", methodCall.arguments[5].toString()) + assertEquals(TRANSFORMED_CODE_TEXT_DISABLED, out) + } + + @Test + fun processClass_textDisabledMultiline() { + val code = StaticJavaParser.parse(TEST_CODE_MULTILINE) + + Mockito.`when`(processor.process(any(CompilationUnit::class.java), + any(ProtoLogCallVisitor::class.java))).thenAnswer { invocation -> + val visitor = invocation.arguments[1] as ProtoLogCallVisitor + + visitor.processCall(code.findAll(MethodCallExpr::class.java)[0], + "test %d %f abc %s\n test", LogLevel.WARN, LogGroup("TEST_GROUP", + true, false, "WM_TEST")) + + invocation.arguments[0] as CompilationUnit + } + + val out = sourceJarWriter.processClass(code) + + val ifStmts = code.findAll(IfStmt::class.java) + assertEquals(1, ifStmts.size) + val ifStmt = ifStmts[0] + assertEquals("TEST_GROUP.${Constants.IS_LOG_TO_ANY_METHOD}()", + ifStmt.condition.toString()) + assertFalse(ifStmt.elseStmt.isPresent) + assertEquals(4, ifStmt.thenStmt.childNodes.size) + val methodCall = ifStmt.thenStmt.findAll(MethodCallExpr::class.java)[1] as MethodCallExpr + assertEquals(PROTO_LOG_IMPL_PATH, methodCall.scope.get().toString()) + assertEquals("w", methodCall.name.asString()) + assertEquals(7, methodCall.arguments.size) + assertEquals("TEST_GROUP", methodCall.arguments[0].toString()) + assertEquals("-986393606", methodCall.arguments[1].toString()) + assertEquals(0b001001.toString(), methodCall.arguments[2].toString()) + assertEquals("null", methodCall.arguments[3].toString()) + assertEquals("protoLogParam0", methodCall.arguments[4].toString()) + assertEquals("protoLogParam1", methodCall.arguments[5].toString()) + assertEquals("protoLogParam2", methodCall.arguments[6].toString()) + assertEquals(TRANSFORMED_CODE_MULTILINE_TEXT_DISABLED, out) + } + + @Test + fun processClass_disabled() { + val code = StaticJavaParser.parse(TEST_CODE) + + Mockito.`when`(processor.process(any(CompilationUnit::class.java), + any(ProtoLogCallVisitor::class.java))).thenAnswer { invocation -> + val visitor = invocation.arguments[1] as ProtoLogCallVisitor + + visitor.processCall(code.findAll(MethodCallExpr::class.java)[0], "test %d %f", + LogLevel.WARN, LogGroup("TEST_GROUP", false, true, "WM_TEST")) + + invocation.arguments[0] as CompilationUnit + } + + val out = sourceJarWriter.processClass(code) + + val ifStmts = code.findAll(IfStmt::class.java) + assertEquals(1, ifStmts.size) + val ifStmt = ifStmts[0] + assertEquals("false", ifStmt.condition.toString()) + assertEquals(TRANSFORMED_CODE_DISABLED, out) + } + + @Test + fun processClass_disabledMultiline() { + val code = StaticJavaParser.parse(TEST_CODE_MULTILINE) + + Mockito.`when`(processor.process(any(CompilationUnit::class.java), + any(ProtoLogCallVisitor::class.java))).thenAnswer { invocation -> + val visitor = invocation.arguments[1] as ProtoLogCallVisitor + + visitor.processCall(code.findAll(MethodCallExpr::class.java)[0], + "test %d %f abc %s\n test", LogLevel.WARN, LogGroup("TEST_GROUP", + false, true, "WM_TEST")) + + invocation.arguments[0] as CompilationUnit + } + + val out = sourceJarWriter.processClass(code) + + val ifStmts = code.findAll(IfStmt::class.java) + assertEquals(1, ifStmts.size) + val ifStmt = ifStmts[0] + assertEquals("false", ifStmt.condition.toString()) + assertEquals(TRANSFORMED_CODE_MULTILINE_DISABLED, out) + } +} diff --git a/tools/protologtool/tests/com/android/protologtool/ViewerConfigBuilderTest.kt b/tools/protologtool/tests/com/android/protologtool/ViewerConfigBuilderTest.kt new file mode 100644 index 000000000000..53d2e8b0f4fa --- /dev/null +++ b/tools/protologtool/tests/com/android/protologtool/ViewerConfigBuilderTest.kt @@ -0,0 +1,120 @@ +/* + * 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.protologtool + +import com.android.json.stream.JsonReader +import com.github.javaparser.ast.CompilationUnit +import com.github.javaparser.ast.expr.MethodCallExpr +import org.junit.Assert.assertEquals +import org.junit.Test +import org.mockito.Mockito +import java.io.StringReader + +class ViewerConfigBuilderTest { + companion object { + private val TAG1 = "WM_TEST" + private val TAG2 = "WM_DEBUG" + private val TEST1 = ViewerConfigParser.ConfigEntry("test1", LogLevel.INFO.name, TAG1) + private val TEST2 = ViewerConfigParser.ConfigEntry("test2", LogLevel.DEBUG.name, TAG2) + private val TEST3 = ViewerConfigParser.ConfigEntry("test3", LogLevel.ERROR.name, TAG2) + } + + private val processor: ProtoLogCallProcessor = Mockito.mock(ProtoLogCallProcessor::class.java) + private val configBuilder = ViewerConfigBuilder(processor) + private val dummyCompilationUnit = CompilationUnit() + + private fun <T> any(type: Class<T>): T = Mockito.any<T>(type) + + private fun parseConfig(json: String): Map<Int, ViewerConfigParser.ConfigEntry> { + return ViewerConfigParser().parseConfig(JsonReader(StringReader(json))) + } + + @Test + fun processClass() { + Mockito.`when`(processor.process(any(CompilationUnit::class.java), + any(ProtoLogCallVisitor::class.java))).thenAnswer { invocation -> + val visitor = invocation.arguments[1] as ProtoLogCallVisitor + + visitor.processCall(MethodCallExpr(), TEST1.messageString, LogLevel.INFO, + LogGroup("TEST_GROUP", true, true, TAG1)) + visitor.processCall(MethodCallExpr(), TEST2.messageString, LogLevel.DEBUG, + LogGroup("DEBUG_GROUP", true, true, TAG2)) + visitor.processCall(MethodCallExpr(), TEST3.messageString, LogLevel.ERROR, + LogGroup("DEBUG_GROUP", true, true, TAG2)) + + invocation.arguments[0] as CompilationUnit + } + + configBuilder.processClass(dummyCompilationUnit) + + val parsedConfig = parseConfig(configBuilder.build()) + assertEquals(3, parsedConfig.size) + assertEquals(TEST1, parsedConfig[CodeUtils.hash(TEST1.messageString, + LogLevel.INFO)]) + assertEquals(TEST2, parsedConfig[CodeUtils.hash(TEST2.messageString, + LogLevel.DEBUG)]) + assertEquals(TEST3, parsedConfig[CodeUtils.hash(TEST3.messageString, + LogLevel.ERROR)]) + } + + @Test + fun processClass_nonUnique() { + Mockito.`when`(processor.process(any(CompilationUnit::class.java), + any(ProtoLogCallVisitor::class.java))).thenAnswer { invocation -> + val visitor = invocation.arguments[1] as ProtoLogCallVisitor + + visitor.processCall(MethodCallExpr(), TEST1.messageString, LogLevel.INFO, + LogGroup("TEST_GROUP", true, true, TAG1)) + visitor.processCall(MethodCallExpr(), TEST1.messageString, LogLevel.INFO, + LogGroup("TEST_GROUP", true, true, TAG1)) + visitor.processCall(MethodCallExpr(), TEST1.messageString, LogLevel.INFO, + LogGroup("TEST_GROUP", true, true, TAG1)) + + invocation.arguments[0] as CompilationUnit + } + + configBuilder.processClass(dummyCompilationUnit) + + val parsedConfig = parseConfig(configBuilder.build()) + assertEquals(1, parsedConfig.size) + assertEquals(TEST1, parsedConfig[CodeUtils.hash(TEST1.messageString, LogLevel.INFO)]) + } + + @Test + fun processClass_disabled() { + Mockito.`when`(processor.process(any(CompilationUnit::class.java), + any(ProtoLogCallVisitor::class.java))).thenAnswer { invocation -> + val visitor = invocation.arguments[1] as ProtoLogCallVisitor + + visitor.processCall(MethodCallExpr(), TEST1.messageString, LogLevel.INFO, + LogGroup("TEST_GROUP", true, true, TAG1)) + visitor.processCall(MethodCallExpr(), TEST2.messageString, LogLevel.DEBUG, + LogGroup("DEBUG_GROUP", false, true, TAG2)) + visitor.processCall(MethodCallExpr(), TEST3.messageString, LogLevel.ERROR, + LogGroup("DEBUG_GROUP", true, false, TAG2)) + + invocation.arguments[0] as CompilationUnit + } + + configBuilder.processClass(dummyCompilationUnit) + + val parsedConfig = parseConfig(configBuilder.build()) + assertEquals(2, parsedConfig.size) + assertEquals(TEST1, parsedConfig[CodeUtils.hash(TEST1.messageString, LogLevel.INFO)]) + assertEquals(TEST3, parsedConfig[CodeUtils.hash(TEST3.messageString, LogLevel.ERROR)]) + } +} diff --git a/tools/protologtool/tests/com/android/protologtool/ViewerConfigParserTest.kt b/tools/protologtool/tests/com/android/protologtool/ViewerConfigParserTest.kt new file mode 100644 index 000000000000..c0cea733eadd --- /dev/null +++ b/tools/protologtool/tests/com/android/protologtool/ViewerConfigParserTest.kt @@ -0,0 +1,327 @@ +/* + * 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.protologtool + +import com.android.json.stream.JsonReader +import org.junit.Test +import java.io.StringReader +import org.junit.Assert.assertEquals + +class ViewerConfigParserTest { + private val parser = ViewerConfigParser() + + private fun getJSONReader(str: String): JsonReader { + return JsonReader(StringReader(str)) + } + + @Test + fun parseMessage() { + val json = """ + { + "message": "Test completed successfully: %b", + "level": "ERROR", + "group": "GENERIC_WM" + } + """ + val msg = parser.parseMessage(getJSONReader(json)) + assertEquals("Test completed successfully: %b", msg.messageString) + assertEquals("ERROR", msg.level) + assertEquals("GENERIC_WM", msg.groupName) + } + + @Test + fun parseMessage_reorder() { + val json = """ + { + "group": "GENERIC_WM", + "level": "ERROR", + "message": "Test completed successfully: %b" + } + """ + val msg = parser.parseMessage(getJSONReader(json)) + assertEquals("Test completed successfully: %b", msg.messageString) + assertEquals("ERROR", msg.level) + assertEquals("GENERIC_WM", msg.groupName) + } + + @Test + fun parseMessage_unknownEntry() { + val json = """ + { + "unknown": "unknown entries should not block parsing", + "message": "Test completed successfully: %b", + "level": "ERROR", + "group": "GENERIC_WM" + } + """ + val msg = parser.parseMessage(getJSONReader(json)) + assertEquals("Test completed successfully: %b", msg.messageString) + assertEquals("ERROR", msg.level) + assertEquals("GENERIC_WM", msg.groupName) + } + + @Test(expected = InvalidViewerConfigException::class) + fun parseMessage_noMessage() { + val json = """ + { + "level": "ERROR", + "group": "GENERIC_WM" + } + """ + parser.parseMessage(getJSONReader(json)) + } + + @Test(expected = InvalidViewerConfigException::class) + fun parseMessage_noLevel() { + val json = """ + { + "message": "Test completed successfully: %b", + "group": "GENERIC_WM" + } + """ + parser.parseMessage(getJSONReader(json)) + } + + @Test(expected = InvalidViewerConfigException::class) + fun parseMessage_noGroup() { + val json = """ + { + "message": "Test completed successfully: %b", + "level": "ERROR" + } + """ + parser.parseMessage(getJSONReader(json)) + } + + @Test + fun parseGroup() { + val json = """ + { + "tag": "WindowManager" + } + """ + val group = parser.parseGroup(getJSONReader(json)) + assertEquals("WindowManager", group.tag) + } + + @Test + fun parseGroup_unknownEntry() { + val json = """ + { + "unknown": "unknown entries should not block parsing", + "tag": "WindowManager" + } + """ + val group = parser.parseGroup(getJSONReader(json)) + assertEquals("WindowManager", group.tag) + } + + @Test(expected = InvalidViewerConfigException::class) + fun parseGroup_noTag() { + val json = """ + { + } + """ + parser.parseGroup(getJSONReader(json)) + } + + @Test + fun parseMessages() { + val json = """ + { + "70933285": { + "message": "Test completed successfully: %b", + "level": "ERROR", + "group": "GENERIC_WM" + }, + "1792430067": { + "message": "Attempted to add window to a display that does not exist: %d. Aborting.", + "level": "WARN", + "group": "ERROR_WM" + } + } + """ + val messages = parser.parseMessages(getJSONReader(json)) + assertEquals(2, messages.size) + val msg1 = + ViewerConfigParser.MessageEntry("Test completed successfully: %b", + "ERROR", "GENERIC_WM") + val msg2 = + ViewerConfigParser.MessageEntry("Attempted to add window to a display that " + + "does not exist: %d. Aborting.", "WARN", "ERROR_WM") + + assertEquals(msg1, messages[70933285]) + assertEquals(msg2, messages[1792430067]) + } + + @Test(expected = InvalidViewerConfigException::class) + fun parseMessages_invalidHash() { + val json = """ + { + "invalid": { + "message": "Test completed successfully: %b", + "level": "ERROR", + "group": "GENERIC_WM" + } + } + """ + parser.parseMessages(getJSONReader(json)) + } + + @Test + fun parseGroups() { + val json = """ + { + "GENERIC_WM": { + "tag": "WindowManager" + }, + "ERROR_WM": { + "tag": "WindowManagerError" + } + } + """ + val groups = parser.parseGroups(getJSONReader(json)) + assertEquals(2, groups.size) + val grp1 = ViewerConfigParser.GroupEntry("WindowManager") + val grp2 = ViewerConfigParser.GroupEntry("WindowManagerError") + assertEquals(grp1, groups["GENERIC_WM"]) + assertEquals(grp2, groups["ERROR_WM"]) + } + + @Test + fun parseConfig() { + val json = """ + { + "version": "${Constants.VERSION}", + "messages": { + "70933285": { + "message": "Test completed successfully: %b", + "level": "ERROR", + "group": "GENERIC_WM" + } + }, + "groups": { + "GENERIC_WM": { + "tag": "WindowManager" + } + } + } + """ + val config = parser.parseConfig(getJSONReader(json)) + assertEquals(1, config.size) + val cfg1 = ViewerConfigParser.ConfigEntry("Test completed successfully: %b", + "ERROR", "WindowManager") + assertEquals(cfg1, config[70933285]) + } + + @Test(expected = InvalidViewerConfigException::class) + fun parseConfig_invalidVersion() { + val json = """ + { + "version": "invalid", + "messages": { + "70933285": { + "message": "Test completed successfully: %b", + "level": "ERROR", + "group": "GENERIC_WM" + } + }, + "groups": { + "GENERIC_WM": { + "tag": "WindowManager" + } + } + } + """ + parser.parseConfig(getJSONReader(json)) + } + + @Test(expected = InvalidViewerConfigException::class) + fun parseConfig_noVersion() { + val json = """ + { + "messages": { + "70933285": { + "message": "Test completed successfully: %b", + "level": "ERROR", + "group": "GENERIC_WM" + } + }, + "groups": { + "GENERIC_WM": { + "tag": "WindowManager" + } + } + } + """ + parser.parseConfig(getJSONReader(json)) + } + + @Test(expected = InvalidViewerConfigException::class) + fun parseConfig_noMessages() { + val json = """ + { + "version": "${Constants.VERSION}", + "groups": { + "GENERIC_WM": { + "tag": "WindowManager" + } + } + } + """ + parser.parseConfig(getJSONReader(json)) + } + + @Test(expected = InvalidViewerConfigException::class) + fun parseConfig_noGroups() { + val json = """ + { + "version": "${Constants.VERSION}", + "messages": { + "70933285": { + "message": "Test completed successfully: %b", + "level": "ERROR", + "group": "GENERIC_WM" + } + } + } + """ + parser.parseConfig(getJSONReader(json)) + } + + @Test(expected = InvalidViewerConfigException::class) + fun parseConfig_missingGroup() { + val json = """ + { + "version": "${Constants.VERSION}", + "messages": { + "70933285": { + "message": "Test completed successfully: %b", + "level": "ERROR", + "group": "GENERIC_WM" + } + }, + "groups": { + "ERROR_WM": { + "tag": "WindowManager" + } + } + } + """ + val config = parser.parseConfig(getJSONReader(json)) + } +} |