summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/system-current.txt4
-rw-r--r--cmds/statsd/Android.bp16
-rw-r--r--cmds/statsd/src/anomaly/AnomalyTracker.cpp3
-rw-r--r--cmds/statsd/src/metrics/metrics_manager_util.cpp4
-rw-r--r--core/java/android/app/ActivityManagerInternal.java37
-rw-r--r--core/java/android/content/Intent.java4
-rw-r--r--core/java/android/content/res/Configuration.java10
-rw-r--r--core/java/android/os/UserHandle.java11
-rw-r--r--core/java/android/os/UserManager.java116
-rw-r--r--core/java/android/os/UserManagerInternal.java2
-rw-r--r--core/java/android/view/SurfaceControl.java26
-rw-r--r--core/java/com/android/internal/app/ChooserActivity.java6
-rw-r--r--core/java/com/android/internal/util/ScreenshotHelper.java77
-rw-r--r--core/jni/android_view_SurfaceControl.cpp11
-rw-r--r--core/proto/Android.bp10
-rw-r--r--core/proto/android/server/windowmanagerlog.proto61
-rw-r--r--core/res/res/values-en-rCA/strings.xml8
-rw-r--r--core/res/res/values-en-rXC/strings.xml8
-rw-r--r--core/tests/screenshothelpertests/Android.bp28
-rw-r--r--core/tests/screenshothelpertests/AndroidManifest.xml32
-rw-r--r--core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java98
-rw-r--r--libs/hwui/service/GraphicsStatsService.cpp6
-rw-r--r--libs/protoutil/include/android/util/ProtoOutputStream.h1
-rw-r--r--libs/protoutil/src/ProtoOutputStream.cpp28
-rw-r--r--packages/BackupEncryption/Android.bp9
-rw-r--r--packages/BackupEncryption/proto/wrapped_key.proto52
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/keys/KeyWrapUtils.java132
-rw-r--r--packages/BackupEncryption/test/robolectric/Android.bp1
-rw-r--r--packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/KeyWrapUtilsTest.java158
-rw-r--r--packages/SettingsLib/res/values-te/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-uz/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-zh-rTW/strings.xml2
-rw-r--r--packages/Shell/res/values-zh-rTW/strings.xml12
-rw-r--r--packages/SystemUI/res/values-en-rCA/strings.xml2
-rw-r--r--packages/SystemUI/res/values-en-rXC/strings.xml2
-rw-r--r--packages/SystemUI/src/com/android/systemui/CornerHandleView.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/AssistManager.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java108
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java31
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java1
-rw-r--r--packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values-ru/strings.xml2
-rw-r--r--packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values-ru/strings.xml2
-rw-r--r--packages/overlays/DisplayCutoutEmulationTallOverlay/res/values-ru/strings.xml2
-rw-r--r--services/accessibility/java/com/android/server/accessibility/GlobalActionPerformer.java5
-rw-r--r--services/core/java/com/android/server/PackageWatchdog.java76
-rw-r--r--services/core/java/com/android/server/SystemService.java10
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java33
-rw-r--r--services/core/java/com/android/server/am/UserController.java89
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java37
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java351
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java3
-rw-r--r--services/core/java/com/android/server/wm/WindowProcessController.java10
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java2
-rw-r--r--services/core/java/com/android/server/wm/WindowSurfaceController.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/GlobalActionPerformerTest.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/StubTransaction.java5
-rw-r--r--telephony/java/android/telephony/ims/feature/ImsFeature.java13
-rw-r--r--telephony/java/android/telephony/ims/feature/MmTelFeature.java4
-rw-r--r--tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java27
-rw-r--r--tests/RollbackTest/Android.bp6
-rw-r--r--tests/RollbackTest/MultiUserRollbackTest.xml (renamed from tests/RollbackTest/SecondaryUserRollbackTest.xml)11
-rw-r--r--tests/RollbackTest/MultiUserRollbackTest/src/com/android/tests/rollback/host/MultiUserRollbackTest.java (renamed from tests/RollbackTest/SecondaryUserRollbackTest/src/com/android/tests/rollback/host/SecondaryUserRollbackTest.java)65
-rw-r--r--tests/RollbackTest/RollbackTest.xml5
-rw-r--r--tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/MultiUserRollbackTest.java111
-rw-r--r--tests/RollbackTest/TEST_MAPPING2
-rw-r--r--tools/protologtool/Android.bp28
-rw-r--r--tools/protologtool/README.md106
-rw-r--r--tools/protologtool/TEST_MAPPING7
-rw-r--r--tools/protologtool/manifest.txt1
-rw-r--r--tools/protologtool/src/com/android/protologtool/CodeUtils.kt135
-rw-r--r--tools/protologtool/src/com/android/protologtool/CommandOptions.kt205
-rw-r--r--tools/protologtool/src/com/android/protologtool/Constants.kt27
-rw-r--r--tools/protologtool/src/com/android/protologtool/LogGroup.kt24
-rw-r--r--tools/protologtool/src/com/android/protologtool/LogLevel.kt37
-rw-r--r--tools/protologtool/src/com/android/protologtool/LogParser.kt112
-rw-r--r--tools/protologtool/src/com/android/protologtool/ProtoLogCallProcessor.kt108
-rw-r--r--tools/protologtool/src/com/android/protologtool/ProtoLogCallVisitor.kt23
-rw-r--r--tools/protologtool/src/com/android/protologtool/ProtoLogGroupReader.kt60
-rw-r--r--tools/protologtool/src/com/android/protologtool/ProtoLogTool.kt94
-rw-r--r--tools/protologtool/src/com/android/protologtool/SourceTransformer.kt162
-rw-r--r--tools/protologtool/src/com/android/protologtool/ViewerConfigBuilder.kt91
-rw-r--r--tools/protologtool/src/com/android/protologtool/ViewerConfigParser.kt125
-rw-r--r--tools/protologtool/src/com/android/protologtool/exceptions.kt44
-rw-r--r--tools/protologtool/tests/com/android/protologtool/CodeUtilsTest.kt206
-rw-r--r--tools/protologtool/tests/com/android/protologtool/CommandOptionsTest.kt250
-rw-r--r--tools/protologtool/tests/com/android/protologtool/LogParserTest.kt187
-rw-r--r--tools/protologtool/tests/com/android/protologtool/ProtoLogCallProcessorTest.kt226
-rw-r--r--tools/protologtool/tests/com/android/protologtool/SourceTransformerTest.kt373
-rw-r--r--tools/protologtool/tests/com/android/protologtool/ViewerConfigBuilderTest.kt120
-rw-r--r--tools/protologtool/tests/com/android/protologtool/ViewerConfigParserTest.kt327
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">"టీథరింగ్ &amp; పోర్టబుల్ హాట్‌స్పాట్"</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 &amp; hold Home to unpin."</string>
<string name="screen_pinning_toast" msgid="2266705122951934150">"To unpin this screen, touch &amp; hold Back and Overview buttons"</string>
<string name="screen_pinning_toast_recents_invisible" msgid="8252402309499161281">"To unpin this screen, touch &amp; hold Back and Home buttons"</string>
- <string name="screen_pinning_toast_gesture_nav" msgid="5070548776081664958">"To unpin this screen, swipe up &amp; 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 &amp; hold Home to unpin.‎‏‎‎‏‎"</string>
<string name="screen_pinning_toast" msgid="2266705122951934150">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‎‏‏‏‎‏‎‎‏‏‏‏‎‏‎‎‎‏‎‎‏‏‎‎‏‏‎‎‎‎‎‎‎‎‎‏‏‎‏‏‏‎‏‏‎‎‎‎‏‏‎‎‎‏‏‎‎To unpin this screen, touch &amp; hold Back and Overview buttons‎‏‎‎‏‎"</string>
<string name="screen_pinning_toast_recents_invisible" msgid="8252402309499161281">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‎‏‎‎‎‎‏‏‎‎‏‏‎‏‏‎‎‎‎‏‎‏‏‎‏‎‏‏‎‏‎‎‏‎‏‎‎‏‏‏‏‏‎‎‎‏‎‏‎‏‏‎‎‎‎‎‏‎To unpin this screen, touch &amp; hold Back and Home buttons‎‏‎‎‏‎"</string>
- <string name="screen_pinning_toast_gesture_nav" msgid="5070548776081664958">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‎‎‏‎‏‏‏‏‎‎‎‏‏‎‏‎‏‎‎‏‏‏‏‏‏‎‎‏‏‎‏‏‎‏‎‏‎‎‎‏‏‏‎‏‎‏‎‏‏‏‎‏‏‏‏‏‎‎To unpin this screen, swipe up &amp; 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))
+ }
+}