Merge "Fix a NPE in ViewRootImpl"
diff --git a/api/current.txt b/api/current.txt
index 9d46863..38a721f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -480,6 +480,10 @@
field public static final int dashGap = 16843175; // 0x10101a7
field public static final int dashWidth = 16843174; // 0x10101a6
field public static final int data = 16842798; // 0x101002e
+ field public static final int dataRetentionTime = 16844189; // 0x101059d
+ field public static final int dataSentOffDevice = 16844186; // 0x101059a
+ field public static final int dataSharedWithThirdParty = 16844187; // 0x101059b
+ field public static final int dataUsedForMonetization = 16844188; // 0x101059c
field public static final int datePickerDialogTheme = 16843948; // 0x10104ac
field public static final int datePickerMode = 16843955; // 0x10104b3
field public static final int datePickerStyle = 16843612; // 0x101035c
@@ -1312,7 +1316,6 @@
field public static final int summaryColumn = 16843426; // 0x10102a2
field public static final int summaryOff = 16843248; // 0x10101f0
field public static final int summaryOn = 16843247; // 0x10101ef
- field public static final int supportsAmbientMode = 16844173; // 0x101058d
field public static final int supportsAssist = 16844016; // 0x10104f0
field public static final int supportsLaunchVoiceAssistFromKeyguard = 16844017; // 0x10104f1
field public static final int supportsLocalInteraction = 16844047; // 0x101050f
@@ -1496,6 +1499,7 @@
field public static final deprecated int unfocusedMonthDateColor = 16843588; // 0x1010344
field public static final int unselectedAlpha = 16843278; // 0x101020e
field public static final int updatePeriodMillis = 16843344; // 0x1010250
+ field public static final int usageInfoRequired = 16844185; // 0x1010599
field public static final int use32bitAbi = 16844053; // 0x1010515
field public static final int useAppZygote = 16844184; // 0x1010598
field public static final int useDefaultMargins = 16843641; // 0x1010379
@@ -6354,7 +6358,6 @@
method public android.graphics.drawable.Drawable loadIcon(android.content.pm.PackageManager);
method public java.lang.CharSequence loadLabel(android.content.pm.PackageManager);
method public android.graphics.drawable.Drawable loadThumbnail(android.content.pm.PackageManager);
- method public boolean supportsAmbientMode();
method public boolean supportsMultipleDisplays();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.app.WallpaperInfo> CREATOR;
@@ -11173,8 +11176,8 @@
field public android.content.pm.ProviderInfo[] providers;
field public android.content.pm.ActivityInfo[] receivers;
field public android.content.pm.FeatureInfo[] reqFeatures;
- field public java.lang.String[] requestedPermissions;
- field public int[] requestedPermissionsFlags;
+ field public deprecated java.lang.String[] requestedPermissions;
+ field public deprecated int[] requestedPermissionsFlags;
field public android.content.pm.ServiceInfo[] services;
field public java.lang.String sharedUserId;
field public int sharedUserLabel;
@@ -11182,6 +11185,7 @@
field public android.content.pm.SigningInfo signingInfo;
field public java.lang.String[] splitNames;
field public int[] splitRevisionCodes;
+ field public android.content.pm.UsesPermissionInfo[] usesPermissions;
field public deprecated int versionCode;
field public java.lang.String versionName;
}
@@ -11657,6 +11661,7 @@
field public java.lang.String group;
field public java.lang.CharSequence nonLocalizedDescription;
field public deprecated int protectionLevel;
+ field public boolean usageInfoRequired;
}
public final class ProviderInfo extends android.content.pm.ComponentInfo implements android.os.Parcelable {
@@ -11836,6 +11841,28 @@
field public static final android.os.Parcelable.Creator<android.content.pm.SigningInfo> CREATOR;
}
+ public final class UsesPermissionInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getDataRetention();
+ method public int getDataRetentionWeeks();
+ method public int getDataSentOffDevice();
+ method public int getDataSharedWithThirdParty();
+ method public int getDataUsedForMonetization();
+ method public int getFlags();
+ method public java.lang.String getPermission();
+ field public static final android.os.Parcelable.Creator<android.content.pm.UsesPermissionInfo> CREATOR;
+ field public static final int FLAG_REQUESTED_PERMISSION_GRANTED = 2; // 0x2
+ field public static final int RETENTION_NOT_RETAINED = 1; // 0x1
+ field public static final int RETENTION_SPECIFIED = 4; // 0x4
+ field public static final int RETENTION_UNDEFINED = 0; // 0x0
+ field public static final int RETENTION_UNLIMITED = 3; // 0x3
+ field public static final int RETENTION_USER_SELECTED = 2; // 0x2
+ field public static final int USAGE_NO = 3; // 0x3
+ field public static final int USAGE_UNDEFINED = 0; // 0x0
+ field public static final int USAGE_USER_TRIGGERED = 2; // 0x2
+ field public static final int USAGE_YES = 1; // 0x1
+ }
+
public final class VersionedPackage implements android.os.Parcelable {
ctor public VersionedPackage(java.lang.String, int);
ctor public VersionedPackage(java.lang.String, long);
@@ -14698,6 +14725,7 @@
method public float getTranslationX();
method public float getTranslationY();
method public float getTranslationZ();
+ method public long getUniqueId();
method public int getWidth();
method public boolean hasDisplayList();
method public boolean hasIdentityMatrix();
@@ -15051,6 +15079,7 @@
method public void invalidateSelf();
method public boolean isAutoMirrored();
method public boolean isFilterBitmap();
+ method public boolean isProjected();
method public boolean isStateful();
method public final boolean isVisible();
method public void jumpToCurrentState();
@@ -29414,11 +29443,24 @@
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.net.wifi.p2p.WifiP2pConfig> CREATOR;
+ field public static final int GROUP_OWNER_BAND_2GHZ = 1; // 0x1
+ field public static final int GROUP_OWNER_BAND_5GHZ = 2; // 0x2
+ field public static final int GROUP_OWNER_BAND_AUTO = 0; // 0x0
field public java.lang.String deviceAddress;
field public int groupOwnerIntent;
field public android.net.wifi.WpsInfo wps;
}
+ public static final class WifiP2pConfig.Builder {
+ ctor public WifiP2pConfig.Builder();
+ method public android.net.wifi.p2p.WifiP2pConfig build();
+ method public android.net.wifi.p2p.WifiP2pConfig.Builder enablePersistentMode(boolean);
+ method public android.net.wifi.p2p.WifiP2pConfig.Builder setDeviceAddress(android.net.MacAddress);
+ method public android.net.wifi.p2p.WifiP2pConfig.Builder setGroupOwnerBand(int);
+ method public android.net.wifi.p2p.WifiP2pConfig.Builder setNetworkName(java.lang.String);
+ method public android.net.wifi.p2p.WifiP2pConfig.Builder setPassphrase(java.lang.String);
+ }
+
public class WifiP2pDevice implements android.os.Parcelable {
ctor public WifiP2pDevice();
ctor public WifiP2pDevice(android.net.wifi.p2p.WifiP2pDevice);
@@ -40899,11 +40941,9 @@
method public int getDesiredMinimumHeight();
method public int getDesiredMinimumWidth();
method public android.view.SurfaceHolder getSurfaceHolder();
- method public boolean isInAmbientMode();
method public boolean isPreview();
method public boolean isVisible();
method public void notifyColorsChanged();
- method public void onAmbientModeChanged(boolean, boolean);
method public void onApplyWindowInsets(android.view.WindowInsets);
method public android.os.Bundle onCommand(java.lang.String, int, int, int, android.os.Bundle, boolean);
method public android.app.WallpaperColors onComputeColors();
diff --git a/api/system-current.txt b/api/system-current.txt
index 257b4f6..b4c7336 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -17,6 +17,7 @@
field public static final java.lang.String ACTIVITY_EMBEDDING = "android.permission.ACTIVITY_EMBEDDING";
field public static final java.lang.String ALLOCATE_AGGRESSIVE = "android.permission.ALLOCATE_AGGRESSIVE";
field public static final java.lang.String ALLOW_ANY_CODEC_FOR_PLAYBACK = "android.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK";
+ field public static final java.lang.String AMBIENT_WALLPAPER = "android.permission.AMBIENT_WALLPAPER";
field public static final java.lang.String BACKUP = "android.permission.BACKUP";
field public static final java.lang.String BATTERY_STATS = "android.permission.BATTERY_STATS";
field public static final java.lang.String BIND_APPWIDGET = "android.permission.BIND_APPWIDGET";
@@ -224,6 +225,7 @@
field public static final int isVrOnly = 16844152; // 0x1010578
field public static final int requiredSystemPropertyName = 16844133; // 0x1010565
field public static final int requiredSystemPropertyValue = 16844134; // 0x1010566
+ field public static final int supportsAmbientMode = 16844173; // 0x101058d
field public static final int userRestriction = 16844164; // 0x1010584
}
@@ -555,6 +557,10 @@
method public void onVrStateChanged(boolean);
}
+ public final class WallpaperInfo implements android.os.Parcelable {
+ method public boolean supportsAmbientMode();
+ }
+
public class WallpaperManager {
method public void clearWallpaper(int, int);
method public void setDisplayOffset(android.os.IBinder, int, int);
@@ -3679,6 +3685,10 @@
public class WifiManager {
method public void connect(android.net.wifi.WifiConfiguration, android.net.wifi.WifiManager.ActionListener);
+ method public void connect(int, android.net.wifi.WifiManager.ActionListener);
+ method public void disable(int, android.net.wifi.WifiManager.ActionListener);
+ method public void disableEphemeralNetwork(java.lang.String);
+ method public void forget(int, android.net.wifi.WifiManager.ActionListener);
method public java.util.List<android.net.wifi.WifiConfiguration> getAllMatchingWifiConfigs(java.util.List<android.net.wifi.ScanResult>);
method public java.util.List<android.net.wifi.hotspot2.OsuProvider> getMatchingOsuProviders(java.util.List<android.net.wifi.ScanResult>);
method public java.util.List<android.net.wifi.WifiConfiguration> getPrivilegedConfiguredNetworks();
@@ -3690,6 +3700,7 @@
method public boolean isWifiApEnabled();
method public boolean isWifiScannerSupported();
method public void registerNetworkRequestMatchCallback(android.net.wifi.WifiManager.NetworkRequestMatchCallback, android.os.Handler);
+ method public void save(android.net.wifi.WifiConfiguration, android.net.wifi.WifiManager.ActionListener);
method public boolean setWifiApConfiguration(android.net.wifi.WifiConfiguration);
method public boolean startScan(android.os.WorkSource);
method public void unregisterNetworkRequestMatchCallback(android.net.wifi.WifiManager.NetworkRequestMatchCallback);
@@ -5334,6 +5345,15 @@
}
+package android.service.wallpaper {
+
+ public class WallpaperService.Engine {
+ method public boolean isInAmbientMode();
+ method public void onAmbientModeChanged(boolean, long);
+ }
+
+}
+
package android.telecom {
public deprecated class AudioState implements android.os.Parcelable {
diff --git a/api/test-current.txt b/api/test-current.txt
index 8c710c1..c0f7ab6 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -61,6 +61,7 @@
}
public class ActivityTaskManager {
+ method public void clearLaunchParamsForPackages(java.util.List<java.lang.String>);
method public java.lang.String listAllStacks();
method public void moveTaskToStack(int, int, boolean);
method public boolean moveTopActivityToPinnedStack(int, android.graphics.Rect);
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index b9732a5..8d61aba 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -679,7 +679,7 @@
string LogEvent::ToString() const {
string result;
- result += StringPrintf("{ %lld %lld (%d)", (long long)mLogdTimestampNs,
+ result += StringPrintf("{ uid(%d) %lld %lld (%d)", mLogUid, (long long)mLogdTimestampNs,
(long long)mElapsedTimestampNs, mTagId);
for (const auto& value : mValues) {
result +=
diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp
index 504c586..f1310db 100644
--- a/cmds/statsd/src/stats_log_util.cpp
+++ b/cmds/statsd/src/stats_log_util.cpp
@@ -343,9 +343,11 @@
}
}
if (isBytesField) {
- protoOutput->write(FIELD_TYPE_MESSAGE | fieldNum,
- (const char*)dim.mValue.str_value.c_str(),
- dim.mValue.str_value.length());
+ if (dim.mValue.str_value.length() > 0) {
+ protoOutput->write(FIELD_TYPE_MESSAGE | fieldNum,
+ (const char*)dim.mValue.str_value.c_str(),
+ dim.mValue.str_value.length());
+ }
} else {
protoOutput->write(FIELD_TYPE_STRING | fieldNum, dim.mValue.str_value);
}
diff --git a/cmds/statsd/tests/LogEvent_test.cpp b/cmds/statsd/tests/LogEvent_test.cpp
index 90dfa87..3a5be43 100644
--- a/cmds/statsd/tests/LogEvent_test.cpp
+++ b/cmds/statsd/tests/LogEvent_test.cpp
@@ -605,7 +605,44 @@
EXPECT_EQ(orig_str, result_str);
}
+TEST(LogEventTest, TestBinaryFieldAtom_empty) {
+ Atom launcherAtom;
+ auto launcher_event = launcherAtom.mutable_launcher_event();
+ launcher_event->set_action(stats::launcher::LauncherAction::LONGPRESS);
+ launcher_event->set_src_state(stats::launcher::LauncherState::OVERVIEW);
+ launcher_event->set_dst_state(stats::launcher::LauncherState::ALLAPPS);
+ // empty string.
+ string extension_str;
+
+ LogEvent event1(Atom::kLauncherEventFieldNumber, 1000);
+
+ event1.write((int32_t)stats::launcher::LauncherAction::LONGPRESS);
+ event1.write((int32_t)stats::launcher::LauncherState::OVERVIEW);
+ event1.write((int64_t)stats::launcher::LauncherState::ALLAPPS);
+ event1.write(extension_str);
+ event1.init();
+
+ ProtoOutputStream proto;
+ event1.ToProto(proto);
+
+ std::vector<uint8_t> outData;
+ outData.resize(proto.size());
+ size_t pos = 0;
+ auto iter = proto.data();
+ while (iter.readBuffer() != NULL) {
+ size_t toRead = iter.currentToRead();
+ std::memcpy(&(outData[pos]), iter.readBuffer(), toRead);
+ pos += toRead;
+ iter.rp()->move(toRead);
+ }
+
+ std::string result_str(outData.begin(), outData.end());
+ std::string orig_str;
+ launcherAtom.SerializeToString(&orig_str);
+
+ EXPECT_EQ(orig_str, result_str);
+}
} // namespace statsd
} // namespace os
diff --git a/config/preloaded-classes b/config/preloaded-classes
index 5940c45..3095925 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -4117,6 +4117,10 @@
com.android.internal.util.VirtualRefBasePtr
com.android.internal.util.XmlUtils
com.android.internal.util.XmlUtils$WriteMapCallback
+com.android.internal.util.function.NonaConsumer
+com.android.internal.util.function.NonaFunction
+com.android.internal.util.function.OctConsumer
+com.android.internal.util.function.OctFunction
com.android.internal.util.function.HeptConsumer
com.android.internal.util.function.HeptFunction
com.android.internal.util.function.HexConsumer
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index 56ccf6f..6fdf7c8 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -433,4 +433,18 @@
}
return sb.toString();
}
+
+ /**
+ * Clears launch params for the given package.
+ * @param packageNames the names of the packages of which the launch params are to be cleared
+ */
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
+ public void clearLaunchParamsForPackages(List<String> packageNames) {
+ try {
+ getService().clearLaunchParamsForPackages(packageNames);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 09b77d5..777a494 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -445,4 +445,9 @@
void setPackageScreenCompatMode(in String packageName, int mode);
boolean getPackageAskScreenCompat(in String packageName);
void setPackageAskScreenCompat(in String packageName, boolean ask);
+
+ /**
+ * Clears launch params for given packages.
+ */
+ void clearLaunchParamsForPackages(in List<String> packageNames);
}
diff --git a/core/java/android/app/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl
index 00547b4..3a2038d 100644
--- a/core/java/android/app/IWallpaperManager.aidl
+++ b/core/java/android/app/IWallpaperManager.aidl
@@ -159,5 +159,5 @@
/**
* Called from SystemUI when it shows the AoD UI.
*/
- oneway void setInAmbientMode(boolean inAmbientMode, boolean animated);
+ oneway void setInAmbientMode(boolean inAmbientMode, long animationDuration);
}
diff --git a/core/java/android/app/WallpaperInfo.java b/core/java/android/app/WallpaperInfo.java
index 3ea3da2..f0f7d89 100644
--- a/core/java/android/app/WallpaperInfo.java
+++ b/core/java/android/app/WallpaperInfo.java
@@ -16,6 +16,7 @@
package android.app;
+import android.annotation.SystemApi;
import android.app.slice.Slice;
import android.content.ComponentName;
import android.content.Context;
@@ -330,7 +331,9 @@
* @see WallpaperService.Engine#onAmbientModeChanged(boolean, boolean)
* @see WallpaperService.Engine#isInAmbientMode()
* @return {@code true} if wallpaper can draw when in ambient mode.
+ * @hide
*/
+ @SystemApi
public boolean supportsAmbientMode() {
return mSupportsAmbientMode;
}
diff --git a/core/java/android/app/backup/OWNERS b/core/java/android/app/backup/OWNERS
index 1c9a43a..673d85f 100644
--- a/core/java/android/app/backup/OWNERS
+++ b/core/java/android/app/backup/OWNERS
@@ -1,7 +1,6 @@
-artikz@google.com
+anniemeng@google.com
brufino@google.com
bryanmawhinney@google.com
ctate@google.com
jorlow@google.com
-mkarpinski@google.com
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index ecdd810..099d15a 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -22,6 +22,9 @@
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* Overall information about the contents of a package. This corresponds
* to all of the information collected from AndroidManifest.xml.
@@ -204,7 +207,10 @@
* {@link PackageManager#GET_PERMISSIONS} was set. This list includes
* all permissions requested, even those that were not granted or known
* by the system at install time.
+ *
+ * @deprecated Use {@link #usesPermissions}
*/
+ @Deprecated
public String[] requestedPermissions;
/**
@@ -214,10 +220,23 @@
* {@link PackageManager#GET_PERMISSIONS} was set. Each value matches
* the corresponding entry in {@link #requestedPermissions}, and will have
* the flag {@link #REQUESTED_PERMISSION_GRANTED} set as appropriate.
+ *
+ * @deprecated Use {@link #usesPermissions}
*/
+ @Deprecated
public int[] requestedPermissionsFlags;
/**
+ * Array of all {@link android.R.styleable#AndroidManifestUsesPermission
+ * <uses-permission>} tags included under <manifest>,
+ * or null if there were none. This is only filled in if the flag
+ * {@link PackageManager#GET_PERMISSIONS} was set. This list includes
+ * all permissions requested, even those that were not granted or known
+ * by the system at install time.
+ */
+ public UsesPermissionInfo[] usesPermissions;
+
+ /**
* Flag for {@link #requestedPermissionsFlags}: the requested permission
* is required for the application to run; the user can not optionally
* disable it. Currently all permissions are required.
@@ -456,6 +475,7 @@
dest.writeTypedArray(permissions, parcelableFlags);
dest.writeStringArray(requestedPermissions);
dest.writeIntArray(requestedPermissionsFlags);
+ dest.writeTypedArray(usesPermissions, parcelableFlags);
dest.writeTypedArray(signatures, parcelableFlags);
dest.writeTypedArray(configPreferences, parcelableFlags);
dest.writeTypedArray(reqFeatures, parcelableFlags);
@@ -520,6 +540,7 @@
permissions = source.createTypedArray(PermissionInfo.CREATOR);
requestedPermissions = source.createStringArray();
requestedPermissionsFlags = source.createIntArray();
+ usesPermissions = source.createTypedArray(UsesPermissionInfo.CREATOR);
signatures = source.createTypedArray(Signature.CREATOR);
configPreferences = source.createTypedArray(ConfigurationInfo.CREATOR);
reqFeatures = source.createTypedArray(FeatureInfo.CREATOR);
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index d00c9a0..49189e5 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -785,18 +785,23 @@
pi.permissions[i] = generatePermissionInfo(p.permissions.get(i), flags);
}
}
- N = p.requestedPermissions.size();
+ N = p.usesPermissionInfos.size();
if (N > 0) {
pi.requestedPermissions = new String[N];
pi.requestedPermissionsFlags = new int[N];
+ pi.usesPermissions = new UsesPermissionInfo[N];
for (int i=0; i<N; i++) {
- final String perm = p.requestedPermissions.get(i);
+ UsesPermissionInfo info = p.usesPermissionInfos.get(i);
+ final String perm = info.getPermission();
pi.requestedPermissions[i] = perm;
+ int permissionFlags = 0;
// The notion of required permissions is deprecated but for compatibility.
- pi.requestedPermissionsFlags[i] |= PackageInfo.REQUESTED_PERMISSION_REQUIRED;
+ permissionFlags |= PackageInfo.REQUESTED_PERMISSION_REQUIRED;
if (grantedPermissions != null && grantedPermissions.contains(perm)) {
- pi.requestedPermissionsFlags[i] |= PackageInfo.REQUESTED_PERMISSION_GRANTED;
+ permissionFlags |= PackageInfo.REQUESTED_PERMISSION_GRANTED;
}
+ pi.requestedPermissionsFlags[i] = permissionFlags;
+ pi.usesPermissions[i] = new UsesPermissionInfo(info, permissionFlags);
}
}
}
@@ -2114,12 +2119,12 @@
return null;
}
} else if (tagName.equals(TAG_USES_PERMISSION)) {
- if (!parseUsesPermission(pkg, res, parser)) {
+ if (!parseUsesPermission(pkg, res, parser, outError)) {
return null;
}
} else if (tagName.equals(TAG_USES_PERMISSION_SDK_M)
|| tagName.equals(TAG_USES_PERMISSION_SDK_23)) {
- if (!parseUsesPermission(pkg, res, parser)) {
+ if (!parseUsesPermission(pkg, res, parser, outError)) {
return null;
}
} else if (tagName.equals(TAG_USES_CONFIGURATION)) {
@@ -2442,7 +2447,7 @@
newPermsMsg.append(' ');
}
newPermsMsg.append(npi.name);
- pkg.requestedPermissions.add(npi.name);
+ addRequestedPermission(pkg, npi.name);
pkg.implicitPermissions.add(npi.name);
}
}
@@ -2463,7 +2468,7 @@
for (int in = 0; in < newPerms.size(); in++) {
final String perm = newPerms.get(in);
if (!pkg.requestedPermissions.contains(perm)) {
- pkg.requestedPermissions.add(perm);
+ addRequestedPermission(pkg, perm);
pkg.implicitPermissions.add(perm);
}
}
@@ -2543,13 +2548,13 @@
}
} else {
if (FORCE_AUDIO_PACKAGES.contains(pkg.packageName)) {
- pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_AUDIO);
+ addRequestedPermission(pkg, android.Manifest.permission.READ_MEDIA_AUDIO);
}
if (FORCE_VIDEO_PACKAGES.contains(pkg.packageName)) {
- pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_VIDEO);
+ addRequestedPermission(pkg, android.Manifest.permission.READ_MEDIA_VIDEO);
}
if (FORCE_IMAGES_PACKAGES.contains(pkg.packageName)) {
- pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_IMAGES);
+ addRequestedPermission(pkg, android.Manifest.permission.READ_MEDIA_IMAGES);
}
}
@@ -2589,6 +2594,14 @@
}
/**
+ * Helper method for adding a requested permission to a package outside of a uses-permission.
+ */
+ private void addRequestedPermission(Package pkg, String permission) {
+ pkg.requestedPermissions.add(permission);
+ pkg.usesPermissionInfos.add(new UsesPermissionInfo(permission));
+ }
+
+ /**
* Computes the targetSdkVersion to use at runtime. If the package is not
* compatible with this platform, populates {@code outError[0]} with an
* error message.
@@ -2845,8 +2858,8 @@
return certSha256Digests;
}
- private boolean parseUsesPermission(Package pkg, Resources res, XmlResourceParser parser)
- throws XmlPullParserException, IOException {
+ private boolean parseUsesPermission(Package pkg, Resources res, XmlResourceParser parser,
+ String[] outError) throws XmlPullParserException, IOException {
TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestUsesPermission);
@@ -2870,6 +2883,44 @@
final String requiredNotfeature = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestUsesPermission_requiredNotFeature, 0);
+ int dataSentOffDevice = sa.getInt(
+ com.android.internal.R.styleable.AndroidManifestUsesPermission_dataSentOffDevice, 0);
+
+ int dataSharedWithThirdParty = sa.getInt(
+ com.android.internal.R.styleable.AndroidManifestUsesPermission_dataSharedWithThirdParty, 0);
+
+ int dataUsedForMonetization = sa.getInt(
+ com.android.internal.R.styleable.AndroidManifestUsesPermission_dataUsedForMonetization, 0);
+
+ int retentionWeeks = -1;
+ int retention;
+
+ String rawRetention = sa.getString(
+ com.android.internal.R.styleable.AndroidManifestUsesPermission_dataRetentionTime);
+
+ if (rawRetention == null) {
+ retention = UsesPermissionInfo.RETENTION_UNDEFINED;
+ } else if ("notRetained".equals(rawRetention)) {
+ retention = UsesPermissionInfo.RETENTION_NOT_RETAINED;
+ } else if ("userSelected".equals(rawRetention)) {
+ retention = UsesPermissionInfo.RETENTION_USER_SELECTED;
+ } else if ("unlimited".equals(rawRetention)) {
+ retention = UsesPermissionInfo.RETENTION_UNLIMITED;
+ } else {
+ // A number of weeks was specified
+ retention = UsesPermissionInfo.RETENTION_SPECIFIED;
+ retentionWeeks = sa.getInt(
+ com.android.internal.R.styleable.AndroidManifestUsesPermission_dataRetentionTime,
+ -1);
+
+ if (retentionWeeks < 0) {
+ outError[0] = "Bad value provided for dataRetentionTime.";
+ mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+ XmlUtils.skipCurrentTag(parser);
+ sa.recycle();
+ return false;
+ }
+ }
sa.recycle();
XmlUtils.skipCurrentTag(parser);
@@ -2902,6 +2953,10 @@
+ parser.getPositionDescription());
}
+ UsesPermissionInfo info = new UsesPermissionInfo(name, dataSentOffDevice,
+ dataSharedWithThirdParty, dataUsedForMonetization, retention, retentionWeeks);
+ pkg.usesPermissionInfos.add(info);
+
return true;
}
@@ -3236,6 +3291,10 @@
perm.info.flags = sa.getInt(
com.android.internal.R.styleable.AndroidManifestPermission_permissionFlags, 0);
+ perm.info.usageInfoRequired = sa.getInt(
+ com.android.internal.R.styleable.AndroidManifestPermission_usageInfoRequired, 0)
+ != 0;
+
sa.recycle();
if (perm.info.protectionLevel == -1) {
@@ -6370,6 +6429,9 @@
@UnsupportedAppUsage
public final ArrayList<String> requestedPermissions = new ArrayList<String>();
+ public final ArrayList<UsesPermissionInfo> usesPermissionInfos =
+ new ArrayList<>();
+
/** Permissions requested but not in the manifest. */
public final ArrayList<String> implicitPermissions = new ArrayList<>();
@@ -6900,6 +6962,7 @@
dest.readStringList(requestedPermissions);
internStringArrayList(requestedPermissions);
+ dest.readParcelableList(usesPermissionInfos, boot);
dest.readStringList(implicitPermissions);
internStringArrayList(implicitPermissions);
protectedBroadcasts = dest.createStringArrayList();
@@ -7066,6 +7129,7 @@
dest.writeParcelableList(instrumentation, flags);
dest.writeStringList(requestedPermissions);
+ dest.writeParcelableList(usesPermissionInfos, flags);
dest.writeStringList(implicitPermissions);
dest.writeStringList(protectedBroadcasts);
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
index e21c33a..be6ed51 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -21,7 +21,6 @@
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
-import static android.content.pm.PackageManager.MATCH_ALL;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
@@ -130,9 +129,6 @@
* </p>
*/
public boolean isMatch(ComponentInfo componentInfo, int flags) {
- if ((flags & MATCH_ALL) != 0) {
- return true;
- }
final boolean isSystemApp = componentInfo.applicationInfo.isSystemApp();
final boolean matchUninstalled = (flags & PackageManager.MATCH_KNOWN_PACKAGES) != 0;
if (!isAvailable(flags)
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index 60c06a1..d9d6b5f 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -20,6 +20,7 @@
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
@@ -308,6 +309,12 @@
*/
public CharSequence nonLocalizedDescription;
+ /**
+ * If {@code true} an application targeting {@link Build.VERSION_CODES#Q} <em>must</em>
+ * include permission data usage information in order to be able to be granted this permission.
+ */
+ public boolean usageInfoRequired;
+
/** @hide */
public static int fixProtectionLevel(int level) {
if (level == PROTECTION_SIGNATURE_OR_SYSTEM) {
@@ -394,6 +401,7 @@
descriptionRes = orig.descriptionRes;
requestRes = orig.requestRes;
nonLocalizedDescription = orig.nonLocalizedDescription;
+ usageInfoRequired = orig.usageInfoRequired;
}
/**
@@ -458,6 +466,7 @@
dest.writeInt(descriptionRes);
dest.writeInt(requestRes);
TextUtils.writeToParcel(nonLocalizedDescription, dest, parcelableFlags);
+ dest.writeInt(usageInfoRequired ? 1 : 0);
}
/** @hide */
@@ -498,5 +507,6 @@
descriptionRes = source.readInt();
requestRes = source.readInt();
nonLocalizedDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+ usageInfoRequired = source.readInt() != 0;
}
}
diff --git a/core/java/android/content/pm/UsesPermissionInfo.java b/core/java/android/content/pm/UsesPermissionInfo.java
new file mode 100644
index 0000000..d08548f
--- /dev/null
+++ b/core/java/android/content/pm/UsesPermissionInfo.java
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import android.annotation.IntDef;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.RetentionPolicy;
+/**
+ * Information you can retrive about a particular application requested permission. This
+ * corresponds to information collected from the AndroidManifest.xml's <uses-permission>
+ * tags.
+ */
+public final class UsesPermissionInfo extends PackageItemInfo implements Parcelable {
+
+ /**
+ * Flag for {@link #getFlags()}: the requested permission is currently granted to the
+ * application.
+ */
+ public static final int FLAG_REQUESTED_PERMISSION_GRANTED = 1 << 1;
+
+ /** @hide */
+ @IntDef(flag = true, prefix = {"FLAG_"}, value = {FLAG_REQUESTED_PERMISSION_GRANTED})
+ @java.lang.annotation.Retention(RetentionPolicy.SOURCE)
+ public @interface Flags {}
+
+ /** An unset value for {@link #getDataSentOffDevice()},
+ * {@link #getDataSharedWithThirdParty()}, and {@link #getDataUsedForMonetization()}
+ */
+ public static final int USAGE_UNDEFINED = 0;
+
+ /**
+ * A yes value for {@link #getDataSentOffDevice()}, {@link #getDataSharedWithThirdParty()},
+ * and {@link #getDataUsedForMonetization()} corresponding to the <code>yes</code> value of
+ * {@link android.R.attr#dataSentOffDevice}, {@link android.R.attr#dataSharedWithThirdParty},
+ * and {@link android.R.attr#dataUsedForMonetization} attributes.
+ */
+ public static final int USAGE_YES = 1;
+
+ /**
+ * A user triggered only value for {@link #getDataSentOffDevice()},
+ * {@link #getDataSharedWithThirdParty()}, and {@link #getDataUsedForMonetization()}
+ * corresponding to the <code>userTriggered</code> value of
+ * {@link android.R.attr#dataSentOffDevice}, {@link android.R.attr#dataSharedWithThirdParty},
+ * and {@link android.R.attr#dataUsedForMonetization} attributes.
+ */
+ public static final int USAGE_USER_TRIGGERED = 2;
+
+ /**
+ * A no value for {@link #getDataSentOffDevice()}, {@link #getDataSharedWithThirdParty()},
+ * and {@link #getDataUsedForMonetization()} corresponding to the <code>no</code> value of
+ * {@link android.R.attr#dataSentOffDevice}, {@link android.R.attr#dataSharedWithThirdParty},
+ * and {@link android.R.attr#dataUsedForMonetization} attributes.
+ */
+ public static final int USAGE_NO = 3;
+
+ /** @hide */
+ @IntDef(prefix = {"USAGE_"}, value = {
+ USAGE_UNDEFINED,
+ USAGE_YES,
+ USAGE_USER_TRIGGERED,
+ USAGE_NO})
+ @java.lang.annotation.Retention(RetentionPolicy.SOURCE)
+ public @interface Usage {}
+
+ /**
+ * An unset value for {@link #getDataRetention}.
+ */
+ public static final int RETENTION_UNDEFINED = 0;
+
+ /**
+ * A data not retained value for {@link #getDataRetention()} corresponding to the
+ * <code>notRetained</code> value of {@link android.R.attr#dataRetentionTime}.
+ */
+ public static final int RETENTION_NOT_RETAINED = 1;
+
+ /**
+ * A user selected value for {@link #getDataRetention()} corresponding to the
+ * <code>userSelected</code> value of {@link android.R.attr#dataRetentionTime}.
+ */
+ public static final int RETENTION_USER_SELECTED = 2;
+
+ /**
+ * An unlimited value for {@link #getDataRetention()} corresponding to the
+ * <code>unlimited</code> value of {@link android.R.attr#dataRetentionTime}.
+ */
+ public static final int RETENTION_UNLIMITED = 3;
+
+ /**
+ * A specified value for {@link #getDataRetention()} corresponding to providing the number of
+ * weeks data is retained in {@link android.R.attr#dataRetentionTime}. The number of weeks
+ * is available in {@link #getDataRetentionWeeks()}.
+ */
+ public static final int RETENTION_SPECIFIED = 4;
+
+ /** @hide */
+ @IntDef(prefix = {"RETENTION_"}, value = {
+ RETENTION_UNDEFINED,
+ RETENTION_NOT_RETAINED,
+ RETENTION_USER_SELECTED,
+ RETENTION_UNLIMITED,
+ RETENTION_SPECIFIED})
+ @java.lang.annotation.Retention(RetentionPolicy.SOURCE)
+ public @interface Retention {}
+
+ private final String mPermission;
+ private final @Flags int mFlags;
+ private final @Usage int mDataSentOffDevice;
+ private final @Usage int mDataSharedWithThirdParty;
+ private final @Usage int mDataUsedForMonetization;
+ private final @Retention int mDataRetention;
+ private final int mDataRetentionWeeks;
+
+ /** @hide */
+ public UsesPermissionInfo(String permission) {
+ mPermission = permission;
+ mDataSentOffDevice = USAGE_UNDEFINED;
+ mDataSharedWithThirdParty = USAGE_UNDEFINED;
+ mDataUsedForMonetization = USAGE_UNDEFINED;
+ mDataRetention = RETENTION_UNDEFINED;
+ mDataRetentionWeeks = -1;
+ mFlags = 0;
+ }
+
+ /** @hide */
+ public UsesPermissionInfo(String permission,
+ @Usage int dataSentOffDevice, @Usage int dataSharedWithThirdParty,
+ @Usage int dataUsedForMonetization, @Retention int dataRetention,
+ int dataRetentionWeeks) {
+ mPermission = permission;
+ mDataSentOffDevice = dataSentOffDevice;
+ mDataSharedWithThirdParty = dataSharedWithThirdParty;
+ mDataUsedForMonetization = dataUsedForMonetization;
+ mDataRetention = dataRetention;
+ mDataRetentionWeeks = dataRetentionWeeks;
+ mFlags = 0;
+ }
+
+ /** @hide */
+ public UsesPermissionInfo(UsesPermissionInfo orig) {
+ this(orig, orig.mFlags);
+ }
+
+ /** @hide */
+ public UsesPermissionInfo(UsesPermissionInfo orig, int flags) {
+ super(orig);
+ mPermission = orig.mPermission;
+ mFlags = flags;
+ mDataSentOffDevice = orig.mDataSentOffDevice;
+ mDataSharedWithThirdParty = orig.mDataSharedWithThirdParty;
+ mDataUsedForMonetization = orig.mDataUsedForMonetization;
+ mDataRetention = orig.mDataRetention;
+ mDataRetentionWeeks = orig.mDataRetentionWeeks;
+ }
+
+ /**
+ * The name of the requested permission.
+ */
+ public String getPermission() {
+ return mPermission;
+ }
+
+ public @Flags int getFlags() {
+ return mFlags;
+ }
+
+ /**
+ * If the application sends the data guarded by this permission off the device.
+ *
+ * See {@link android.R.attr#dataSentOffDevice}
+ */
+ public @Usage int getDataSentOffDevice() {
+ return mDataSentOffDevice;
+ }
+
+ /**
+ * If the application or its services shares the data guarded by this permission with third
+ * parties.
+ *
+ * See {@link android.R.attr#dataSharedWithThirdParty}
+ */
+ public @Usage int getDataSharedWithThirdParty() {
+ return mDataSharedWithThirdParty;
+ }
+
+ /**
+ * If the application or its services use the data guarded by this permission for monetization
+ * purposes.
+ *
+ * See {@link android.R.attr#dataUsedForMonetization}
+ */
+ public @Usage int getDataUsedForMonetization() {
+ return mDataUsedForMonetization;
+ }
+
+ /**
+ * How long the application or its services store the data guarded by this permission.
+ * If set to {@link #RETENTION_SPECIFIED} {@link #getDataRetentionWeeks()} will contain the
+ * number of weeks the data is stored.
+ *
+ * See {@link android.R.attr#dataRetentionTime}
+ */
+ public @Retention int getDataRetention() {
+ return mDataRetention;
+ }
+
+ /**
+ * If {@link #getDataRetention()} is {@link #RETENTION_SPECIFIED} the number of weeks the
+ * application or its services store data guarded by this permission.
+ *
+ * @throws IllegalStateException if {@link #getDataRetention} is not
+ * {@link #RETENTION_SPECIFIED}.
+ */
+ public int getDataRetentionWeeks() {
+ if (mDataRetention != RETENTION_SPECIFIED) {
+ throw new IllegalStateException("Data retention weeks not specified");
+ }
+ return mDataRetentionWeeks;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeString(mPermission);
+ dest.writeInt(mFlags);
+ dest.writeInt(mDataSentOffDevice);
+ dest.writeInt(mDataSharedWithThirdParty);
+ dest.writeInt(mDataUsedForMonetization);
+ dest.writeInt(mDataRetention);
+ dest.writeInt(mDataRetentionWeeks);
+ }
+
+ private UsesPermissionInfo(Parcel source) {
+ super(source);
+ mPermission = source.readString();
+ mFlags = source.readInt();
+ mDataSentOffDevice = source.readInt();
+ mDataSharedWithThirdParty = source.readInt();
+ mDataUsedForMonetization = source.readInt();
+ mDataRetention = source.readInt();
+ mDataRetentionWeeks = source.readInt();
+ }
+
+ public static final Creator<UsesPermissionInfo> CREATOR =
+ new Creator<UsesPermissionInfo>() {
+ @Override
+ public UsesPermissionInfo createFromParcel(Parcel source) {
+ return new UsesPermissionInfo(source);
+ }
+ @Override
+ public UsesPermissionInfo[] newArray(int size) {
+ return new UsesPermissionInfo[size];
+ }
+ };
+}
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 651caec..2abcb4c 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -1055,6 +1055,9 @@
*/
public static final native long getPss(int pid);
+ /** @hide */
+ public static final native long[] getRss(int pid);
+
/**
* Specifies the outcome of having started a process.
* @hide
diff --git a/core/java/android/service/wallpaper/IWallpaperEngine.aidl b/core/java/android/service/wallpaper/IWallpaperEngine.aidl
index dccce40..ebce484 100644
--- a/core/java/android/service/wallpaper/IWallpaperEngine.aidl
+++ b/core/java/android/service/wallpaper/IWallpaperEngine.aidl
@@ -27,7 +27,7 @@
void setDesiredSize(int width, int height);
void setDisplayPadding(in Rect padding);
void setVisibility(boolean visible);
- void setInAmbientMode(boolean inAmbientDisplay, boolean animated);
+ void setInAmbientMode(boolean inAmbientDisplay, long animationDuration);
void dispatchPointer(in MotionEvent event);
void dispatchWallpaperCommand(String action, int x, int y,
int z, in Bundle extras);
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 45d53f3..a095b0d 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -19,6 +19,7 @@
import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemApi;
import android.annotation.UnsupportedAppUsage;
import android.app.Service;
import android.app.WallpaperColors;
@@ -442,7 +443,9 @@
/**
* Returns true if this engine is running in ambient mode -- that is,
* it is being shown in low power mode, on always on display.
+ * @hide
*/
+ @SystemApi
public boolean isInAmbientMode() {
return mIsInAmbientMode;
}
@@ -568,14 +571,16 @@
* Called when the device enters or exits ambient mode.
*
* @param inAmbientMode {@code true} if in ambient mode.
- * @param animated {@code true} if you'll have the opportunity of animating your transition
- * {@code false} when the wallpaper should present its ambient version
- * immediately.
+ * @param animationDuration How long the transition animation to change the ambient state
+ * should run, in milliseconds. If 0 is passed as the argument
+ * here, the state should be switched immediately.
*
* @see #isInAmbientMode()
* @see WallpaperInfo#supportsAmbientMode()
+ * @hide
*/
- public void onAmbientModeChanged(boolean inAmbientMode, boolean animated) {
+ @SystemApi
+ public void onAmbientModeChanged(boolean inAmbientMode, long animationDuration) {
}
/**
@@ -1049,19 +1054,19 @@
* message sent from handler.
*
* @param inAmbientMode {@code true} if in ambient mode.
- * @param animated {@code true} if the transition will be animated.
+ * @param animationDuration For how long the transition will last, in ms.
* @hide
*/
@VisibleForTesting
- public void doAmbientModeChanged(boolean inAmbientMode, boolean animated) {
+ public void doAmbientModeChanged(boolean inAmbientMode, long animationDuration) {
if (!mDestroyed) {
if (DEBUG) {
Log.v(TAG, "onAmbientModeChanged(" + inAmbientMode + ", "
- + animated + "): " + this);
+ + animationDuration + "): " + this);
}
mIsInAmbientMode = inAmbientMode;
if (mCreated) {
- onAmbientModeChanged(inAmbientMode, animated);
+ onAmbientModeChanged(inAmbientMode, animationDuration);
}
}
}
@@ -1320,10 +1325,10 @@
}
@Override
- public void setInAmbientMode(boolean inAmbientDisplay, boolean animated)
+ public void setInAmbientMode(boolean inAmbientDisplay, long animationDuration)
throws RemoteException {
- Message msg = mCaller.obtainMessageII(DO_IN_AMBIENT_MODE, inAmbientDisplay ? 1 : 0,
- animated ? 1 : 0);
+ Message msg = mCaller.obtainMessageIO(DO_IN_AMBIENT_MODE, inAmbientDisplay ? 1 : 0,
+ animationDuration);
mCaller.sendMessage(msg);
}
@@ -1394,7 +1399,7 @@
return;
}
case DO_IN_AMBIENT_MODE: {
- mEngine.doAmbientModeChanged(message.arg1 != 0, message.arg2 != 0);
+ mEngine.doAmbientModeChanged(message.arg1 != 0, (Long) message.obj);
return;
}
case MSG_UPDATE_SURFACE:
diff --git a/core/java/com/android/internal/util/function/NonaConsumer.java b/core/java/com/android/internal/util/function/NonaConsumer.java
new file mode 100644
index 0000000..3e7ce2b
--- /dev/null
+++ b/core/java/com/android/internal/util/function/NonaConsumer.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util.function;
+
+import java.util.function.Consumer;
+
+/**
+ * A 9-argument {@link Consumer}
+ *
+ * @hide
+ */
+public interface NonaConsumer<A, B, C, D, E, F, G, H, I> {
+ void accept(A a, B b, C c, D d, E e, F f, G g, H h, I i);
+}
diff --git a/core/java/com/android/internal/util/function/NonaFunction.java b/core/java/com/android/internal/util/function/NonaFunction.java
new file mode 100644
index 0000000..560b4f1
--- /dev/null
+++ b/core/java/com/android/internal/util/function/NonaFunction.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util.function;
+
+import java.util.function.Function;
+
+/**
+ * A 9-argument {@link Function}
+ *
+ * @hide
+ */
+public interface NonaFunction<A, B, C, D, E, F, G, H, I, R> {
+ R apply(A a, B b, C c, D d, E e, F f, G g, H h, I i);
+}
diff --git a/core/java/com/android/internal/util/function/NonaPredicate.java b/core/java/com/android/internal/util/function/NonaPredicate.java
new file mode 100644
index 0000000..c1e6f37
--- /dev/null
+++ b/core/java/com/android/internal/util/function/NonaPredicate.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util.function;
+
+import java.util.function.Predicate;
+
+/**
+ * A 9-argument {@link Predicate}
+ *
+ * @hide
+ */
+public interface NonaPredicate<A, B, C, D, E, F, G, H, I> {
+ boolean test(A a, B b, C c, D d, E e, F f, G g, H h, I i);
+}
diff --git a/core/java/com/android/internal/util/function/OctConsumer.java b/core/java/com/android/internal/util/function/OctConsumer.java
new file mode 100644
index 0000000..83ee305
--- /dev/null
+++ b/core/java/com/android/internal/util/function/OctConsumer.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util.function;
+
+import java.util.function.Consumer;
+
+/**
+ * A 8-argument {@link Consumer}
+ *
+ * @hide
+ */
+public interface OctConsumer<A, B, C, D, E, F, G, H> {
+ void accept(A a, B b, C c, D d, E e, F f, G g, H h);
+}
diff --git a/core/java/com/android/internal/util/function/OctFunction.java b/core/java/com/android/internal/util/function/OctFunction.java
new file mode 100644
index 0000000..cb16624
--- /dev/null
+++ b/core/java/com/android/internal/util/function/OctFunction.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util.function;
+
+import java.util.function.Function;
+
+/**
+ * A 8-argument {@link Function}
+ *
+ * @hide
+ */
+public interface OctFunction<A, B, C, D, E, F, G, H, R> {
+ R apply(A a, B b, C c, D d, E e, F f, G g, H h);
+}
diff --git a/core/java/com/android/internal/util/function/OctPredicate.java b/core/java/com/android/internal/util/function/OctPredicate.java
new file mode 100644
index 0000000..7f36d6ac
--- /dev/null
+++ b/core/java/com/android/internal/util/function/OctPredicate.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util.function;
+
+import java.util.function.Predicate;
+
+/**
+ * A 8-argument {@link Predicate}
+ *
+ * @hide
+ */
+public interface OctPredicate<A, B, C, D, E, F, G, H> {
+ boolean test(A a, B b, C c, D d, E e, F f, G g, H h);
+}
diff --git a/core/java/com/android/internal/util/function/pooled/OmniFunction.java b/core/java/com/android/internal/util/function/pooled/OmniFunction.java
index 4ffe441..d74e715 100755
--- a/core/java/com/android/internal/util/function/pooled/OmniFunction.java
+++ b/core/java/com/android/internal/util/function/pooled/OmniFunction.java
@@ -22,6 +22,10 @@
import com.android.internal.util.function.HeptFunction;
import com.android.internal.util.function.HexConsumer;
import com.android.internal.util.function.HexFunction;
+import com.android.internal.util.function.NonaConsumer;
+import com.android.internal.util.function.NonaFunction;
+import com.android.internal.util.function.OctConsumer;
+import com.android.internal.util.function.OctFunction;
import com.android.internal.util.function.QuadConsumer;
import com.android.internal.util.function.QuadFunction;
import com.android.internal.util.function.QuintConsumer;
@@ -39,61 +43,62 @@
*
* @hide
*/
-abstract class OmniFunction<A, B, C, D, E, F, G, R> implements
+abstract class OmniFunction<A, B, C, D, E, F, G, H, I, R> implements
PooledFunction<A, R>, BiFunction<A, B, R>, TriFunction<A, B, C, R>,
QuadFunction<A, B, C, D, R>, QuintFunction<A, B, C, D, E, R>,
HexFunction<A, B, C, D, E, F, R>, HeptFunction<A, B, C, D, E, F, G, R>,
+ OctFunction<A, B, C, D, E, F, G, H, R>, NonaFunction<A, B, C, D, E, F, G, H, I, R>,
PooledConsumer<A>, BiConsumer<A, B>, TriConsumer<A, B, C>, QuadConsumer<A, B, C, D>,
QuintConsumer<A, B, C, D, E>, HexConsumer<A, B, C, D, E, F>,
- HeptConsumer<A, B, C, D, E, F, G>,
- PooledPredicate<A>, BiPredicate<A, B>,
+ HeptConsumer<A, B, C, D, E, F, G>, OctConsumer<A, B, C, D, E, F, G, H>,
+ NonaConsumer<A, B, C, D, E, F, G, H, I>, PooledPredicate<A>, BiPredicate<A, B>,
PooledSupplier<R>, PooledRunnable, ThrowingRunnable, ThrowingSupplier<R>,
PooledSupplier.OfInt, PooledSupplier.OfLong, PooledSupplier.OfDouble {
- abstract R invoke(A a, B b, C c, D d, E e, F f, G g);
+ abstract R invoke(A a, B b, C c, D d, E e, F f, G g, H h, I i);
@Override
public R apply(A o, B o2) {
- return invoke(o, o2, null, null, null, null, null);
+ return invoke(o, o2, null, null, null, null, null, null, null);
}
@Override
public R apply(A o) {
- return invoke(o, null, null, null, null, null, null);
+ return invoke(o, null, null, null, null, null, null, null, null);
}
- public abstract <V> OmniFunction<A, B, C, D, E, F, G, V> andThen(
+ public abstract <V> OmniFunction<A, B, C, D, E, F, G, H, I, V> andThen(
Function<? super R, ? extends V> after);
- public abstract OmniFunction<A, B, C, D, E, F, G, R> negate();
+ public abstract OmniFunction<A, B, C, D, E, F, G, H, I, R> negate();
@Override
public void accept(A o, B o2) {
- invoke(o, o2, null, null, null, null, null);
+ invoke(o, o2, null, null, null, null, null, null, null);
}
@Override
public void accept(A o) {
- invoke(o, null, null, null, null, null, null);
+ invoke(o, null, null, null, null, null, null, null, null);
}
@Override
public void run() {
- invoke(null, null, null, null, null, null, null);
+ invoke(null, null, null, null, null, null, null, null, null);
}
@Override
public R get() {
- return invoke(null, null, null, null, null, null, null);
+ return invoke(null, null, null, null, null, null, null, null, null);
}
@Override
public boolean test(A o, B o2) {
- return (Boolean) invoke(o, o2, null, null, null, null, null);
+ return (Boolean) invoke(o, o2, null, null, null, null, null, null, null);
}
@Override
public boolean test(A o) {
- return (Boolean) invoke(o, null, null, null, null, null, null);
+ return (Boolean) invoke(o, null, null, null, null, null, null, null, null);
}
@Override
@@ -108,52 +113,72 @@
@Override
public R apply(A a, B b, C c) {
- return invoke(a, b, c, null, null, null, null);
+ return invoke(a, b, c, null, null, null, null, null, null);
}
@Override
public void accept(A a, B b, C c) {
- invoke(a, b, c, null, null, null, null);
+ invoke(a, b, c, null, null, null, null, null, null);
}
@Override
public R apply(A a, B b, C c, D d) {
- return invoke(a, b, c, d, null, null, null);
+ return invoke(a, b, c, d, null, null, null, null, null);
}
@Override
public R apply(A a, B b, C c, D d, E e) {
- return invoke(a, b, c, d, e, null, null);
+ return invoke(a, b, c, d, e, null, null, null, null);
}
@Override
public R apply(A a, B b, C c, D d, E e, F f) {
- return invoke(a, b, c, d, e, f, null);
+ return invoke(a, b, c, d, e, f, null, null, null);
}
@Override
public R apply(A a, B b, C c, D d, E e, F f, G g) {
- return invoke(a, b, c, d, e, f, g);
+ return invoke(a, b, c, d, e, f, g, null, null);
+ }
+
+ @Override
+ public R apply(A a, B b, C c, D d, E e, F f, G g, H h) {
+ return invoke(a, b, c, d, e, f, g, h, null);
+ }
+
+ @Override
+ public R apply(A a, B b, C c, D d, E e, F f, G g, H h, I i) {
+ return invoke(a, b, c, d, e, f, g, h, i);
}
@Override
public void accept(A a, B b, C c, D d) {
- invoke(a, b, c, d, null, null, null);
+ invoke(a, b, c, d, null, null, null, null, null);
}
@Override
public void accept(A a, B b, C c, D d, E e) {
- invoke(a, b, c, d, e, null, null);
+ invoke(a, b, c, d, e, null, null, null, null);
}
@Override
public void accept(A a, B b, C c, D d, E e, F f) {
- invoke(a, b, c, d, e, f, null);
+ invoke(a, b, c, d, e, f, null, null, null);
}
@Override
public void accept(A a, B b, C c, D d, E e, F f, G g) {
- invoke(a, b, c, d, e, f, g);
+ invoke(a, b, c, d, e, f, g, null, null);
+ }
+
+ @Override
+ public void accept(A a, B b, C c, D d, E e, F f, G g, H h) {
+ invoke(a, b, c, d, e, f, g, h, null);
+ }
+
+ @Override
+ public void accept(A a, B b, C c, D d, E e, F f, G g, H h, I i) {
+ invoke(a, b, c, d, e, f, g, h, i);
}
@Override
@@ -167,5 +192,5 @@
}
@Override
- public abstract OmniFunction<A, B, C, D, E, F, G, R> recycleOnUse();
+ public abstract OmniFunction<A, B, C, D, E, F, G, H, I, R> recycleOnUse();
}
diff --git a/core/java/com/android/internal/util/function/pooled/PooledLambda.java b/core/java/com/android/internal/util/function/pooled/PooledLambda.java
index af3c752..c00932e 100755
--- a/core/java/com/android/internal/util/function/pooled/PooledLambda.java
+++ b/core/java/com/android/internal/util/function/pooled/PooledLambda.java
@@ -25,6 +25,10 @@
import com.android.internal.util.function.HeptFunction;
import com.android.internal.util.function.HexConsumer;
import com.android.internal.util.function.HexFunction;
+import com.android.internal.util.function.NonaConsumer;
+import com.android.internal.util.function.NonaFunction;
+import com.android.internal.util.function.OctConsumer;
+import com.android.internal.util.function.OctFunction;
import com.android.internal.util.function.QuadConsumer;
import com.android.internal.util.function.QuadFunction;
import com.android.internal.util.function.QuintConsumer;
@@ -176,7 +180,8 @@
Consumer<? super A> function,
A arg1) {
return acquire(PooledLambdaImpl.sPool,
- function, 1, 0, ReturnType.VOID, arg1, null, null, null, null, null, null);
+ function, 1, 0, ReturnType.VOID, arg1, null, null, null, null, null, null, null,
+ null);
}
/**
@@ -192,7 +197,8 @@
Predicate<? super A> function,
A arg1) {
return acquire(PooledLambdaImpl.sPool,
- function, 1, 0, ReturnType.BOOLEAN, arg1, null, null, null, null, null, null);
+ function, 1, 0, ReturnType.BOOLEAN, arg1, null, null, null, null, null, null, null,
+ null);
}
/**
@@ -208,7 +214,8 @@
Function<? super A, ? extends R> function,
A arg1) {
return acquire(PooledLambdaImpl.sPool,
- function, 1, 0, ReturnType.OBJECT, arg1, null, null, null, null, null, null);
+ function, 1, 0, ReturnType.OBJECT, arg1, null, null, null, null, null, null, null,
+ null);
}
/**
@@ -238,7 +245,8 @@
A arg1) {
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
- function, 1, 0, ReturnType.VOID, arg1, null, null, null, null, null, null);
+ function, 1, 0, ReturnType.VOID, arg1, null, null, null, null, null, null, null,
+ null);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
@@ -257,7 +265,8 @@
BiConsumer<? super A, ? super B> function,
A arg1, B arg2) {
return acquire(PooledLambdaImpl.sPool,
- function, 2, 0, ReturnType.VOID, arg1, arg2, null, null, null, null, null);
+ function, 2, 0, ReturnType.VOID, arg1, arg2, null, null, null, null, null, null,
+ null);
}
/**
@@ -274,7 +283,8 @@
BiPredicate<? super A, ? super B> function,
A arg1, B arg2) {
return acquire(PooledLambdaImpl.sPool,
- function, 2, 0, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null);
+ function, 2, 0, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null, null,
+ null);
}
/**
@@ -291,7 +301,8 @@
BiFunction<? super A, ? super B, ? extends R> function,
A arg1, B arg2) {
return acquire(PooledLambdaImpl.sPool,
- function, 2, 0, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null);
+ function, 2, 0, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null, null,
+ null);
}
/**
@@ -308,7 +319,8 @@
BiConsumer<? super A, ? super B> function,
ArgumentPlaceholder<A> arg1, B arg2) {
return acquire(PooledLambdaImpl.sPool,
- function, 2, 1, ReturnType.VOID, arg1, arg2, null, null, null, null, null);
+ function, 2, 1, ReturnType.VOID, arg1, arg2, null, null, null, null, null, null,
+ null);
}
/**
@@ -325,7 +337,8 @@
BiPredicate<? super A, ? super B> function,
ArgumentPlaceholder<A> arg1, B arg2) {
return acquire(PooledLambdaImpl.sPool,
- function, 2, 1, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null);
+ function, 2, 1, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null, null,
+ null);
}
/**
@@ -342,7 +355,8 @@
BiFunction<? super A, ? super B, ? extends R> function,
ArgumentPlaceholder<A> arg1, B arg2) {
return acquire(PooledLambdaImpl.sPool,
- function, 2, 1, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null);
+ function, 2, 1, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null, null,
+ null);
}
/**
@@ -359,7 +373,8 @@
BiConsumer<? super A, ? super B> function,
A arg1, ArgumentPlaceholder<B> arg2) {
return acquire(PooledLambdaImpl.sPool,
- function, 2, 1, ReturnType.VOID, arg1, arg2, null, null, null, null, null);
+ function, 2, 1, ReturnType.VOID, arg1, arg2, null, null, null, null, null, null,
+ null);
}
/**
@@ -376,7 +391,8 @@
BiPredicate<? super A, ? super B> function,
A arg1, ArgumentPlaceholder<B> arg2) {
return acquire(PooledLambdaImpl.sPool,
- function, 2, 1, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null);
+ function, 2, 1, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null, null,
+ null);
}
/**
@@ -393,7 +409,8 @@
BiFunction<? super A, ? super B, ? extends R> function,
A arg1, ArgumentPlaceholder<B> arg2) {
return acquire(PooledLambdaImpl.sPool,
- function, 2, 1, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null);
+ function, 2, 1, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null, null,
+ null);
}
/**
@@ -424,7 +441,8 @@
A arg1, B arg2) {
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
- function, 2, 0, ReturnType.VOID, arg1, arg2, null, null, null, null, null);
+ function, 2, 0, ReturnType.VOID, arg1, arg2, null, null, null, null, null, null,
+ null);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
@@ -444,7 +462,8 @@
TriConsumer<? super A, ? super B, ? super C> function,
A arg1, B arg2, C arg3) {
return acquire(PooledLambdaImpl.sPool,
- function, 3, 0, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null);
+ function, 3, 0, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null,
+ null);
}
/**
@@ -462,7 +481,8 @@
TriFunction<? super A, ? super B, ? super C, ? extends R> function,
A arg1, B arg2, C arg3) {
return acquire(PooledLambdaImpl.sPool,
- function, 3, 0, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null);
+ function, 3, 0, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null, null,
+ null);
}
/**
@@ -480,7 +500,8 @@
TriConsumer<? super A, ? super B, ? super C> function,
ArgumentPlaceholder<A> arg1, B arg2, C arg3) {
return acquire(PooledLambdaImpl.sPool,
- function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null);
+ function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null,
+ null);
}
/**
@@ -498,7 +519,8 @@
TriFunction<? super A, ? super B, ? super C, ? extends R> function,
ArgumentPlaceholder<A> arg1, B arg2, C arg3) {
return acquire(PooledLambdaImpl.sPool,
- function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null);
+ function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null, null,
+ null);
}
/**
@@ -516,7 +538,8 @@
TriConsumer<? super A, ? super B, ? super C> function,
A arg1, ArgumentPlaceholder<B> arg2, C arg3) {
return acquire(PooledLambdaImpl.sPool,
- function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null);
+ function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null,
+ null);
}
/**
@@ -534,7 +557,8 @@
TriFunction<? super A, ? super B, ? super C, ? extends R> function,
A arg1, ArgumentPlaceholder<B> arg2, C arg3) {
return acquire(PooledLambdaImpl.sPool,
- function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null);
+ function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null, null,
+ null);
}
/**
@@ -552,7 +576,8 @@
TriConsumer<? super A, ? super B, ? super C> function,
A arg1, B arg2, ArgumentPlaceholder<C> arg3) {
return acquire(PooledLambdaImpl.sPool,
- function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null);
+ function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null,
+ null);
}
/**
@@ -570,7 +595,8 @@
TriFunction<? super A, ? super B, ? super C, ? extends R> function,
A arg1, B arg2, ArgumentPlaceholder<C> arg3) {
return acquire(PooledLambdaImpl.sPool,
- function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null);
+ function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null, null,
+ null);
}
/**
@@ -602,7 +628,8 @@
A arg1, B arg2, C arg3) {
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
- function, 3, 0, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null);
+ function, 3, 0, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null,
+ null);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
@@ -623,7 +650,8 @@
QuadConsumer<? super A, ? super B, ? super C, ? super D> function,
A arg1, B arg2, C arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
- function, 4, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null);
+ function, 4, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null,
+ null);
}
/**
@@ -642,7 +670,8 @@
QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function,
A arg1, B arg2, C arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
- function, 4, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null);
+ function, 4, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null,
+ null);
}
/**
@@ -661,7 +690,8 @@
QuadConsumer<? super A, ? super B, ? super C, ? super D> function,
ArgumentPlaceholder<A> arg1, B arg2, C arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
- function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null);
+ function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null,
+ null);
}
/**
@@ -680,7 +710,8 @@
QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function,
ArgumentPlaceholder<A> arg1, B arg2, C arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
- function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null);
+ function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null,
+ null);
}
/**
@@ -699,7 +730,8 @@
QuadConsumer<? super A, ? super B, ? super C, ? super D> function,
A arg1, ArgumentPlaceholder<B> arg2, C arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
- function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null);
+ function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null,
+ null);
}
/**
@@ -718,7 +750,8 @@
QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function,
A arg1, ArgumentPlaceholder<B> arg2, C arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
- function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null);
+ function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null,
+ null);
}
/**
@@ -737,7 +770,8 @@
QuadConsumer<? super A, ? super B, ? super C, ? super D> function,
A arg1, B arg2, ArgumentPlaceholder<C> arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
- function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null);
+ function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null,
+ null);
}
/**
@@ -756,7 +790,8 @@
QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function,
A arg1, B arg2, ArgumentPlaceholder<C> arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
- function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null);
+ function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null,
+ null);
}
/**
@@ -775,7 +810,8 @@
QuadConsumer<? super A, ? super B, ? super C, ? super D> function,
A arg1, B arg2, C arg3, ArgumentPlaceholder<D> arg4) {
return acquire(PooledLambdaImpl.sPool,
- function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null);
+ function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null,
+ null);
}
/**
@@ -794,7 +830,8 @@
QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function,
A arg1, B arg2, C arg3, ArgumentPlaceholder<D> arg4) {
return acquire(PooledLambdaImpl.sPool,
- function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null);
+ function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null,
+ null);
}
/**
@@ -827,7 +864,8 @@
A arg1, B arg2, C arg3, D arg4) {
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
- function, 4, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null);
+ function, 4, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null,
+ null);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
@@ -849,7 +887,8 @@
QuintConsumer<? super A, ? super B, ? super C, ? super D, ? super E> function,
A arg1, B arg2, C arg3, D arg4, E arg5) {
return acquire(PooledLambdaImpl.sPool,
- function, 5, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, null, null);
+ function, 5, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, null, null, null,
+ null);
}
/**
@@ -869,7 +908,8 @@
QuintFunction<? super A, ? super B, ? super C, ? super D, ? super E, ? extends R>
function, A arg1, B arg2, C arg3, D arg4, E arg5) {
return acquire(PooledLambdaImpl.sPool,
- function, 5, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, null, null);
+ function, 5, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, null, null, null,
+ null);
}
/**
@@ -904,7 +944,8 @@
A arg1, B arg2, C arg3, D arg4, E arg5) {
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
- function, 5, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, null, null);
+ function, 5, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, null, null, null,
+ null);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
@@ -927,7 +968,8 @@
HexConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F> function,
A arg1, B arg2, C arg3, D arg4, E arg5, F arg6) {
return acquire(PooledLambdaImpl.sPool,
- function, 6, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, null);
+ function, 6, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, null, null,
+ null);
}
/**
@@ -948,7 +990,8 @@
HexFunction<? super A, ? super B, ? super C, ? super D, ? super E, ? super F,
? extends R> function, A arg1, B arg2, C arg3, D arg4, E arg5, F arg6) {
return acquire(PooledLambdaImpl.sPool,
- function, 6, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, null);
+ function, 6, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, null, null,
+ null);
}
/**
@@ -984,7 +1027,8 @@
A arg1, B arg2, C arg3, D arg4, E arg5, F arg6) {
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
- function, 6, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, null);
+ function, 6, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, null, null,
+ null);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
@@ -1008,7 +1052,8 @@
HeptConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F,
? super G> function, A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7) {
return acquire(PooledLambdaImpl.sPool,
- function, 7, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
+ function, 7, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, null,
+ null);
}
/**
@@ -1031,7 +1076,8 @@
? super G, ? extends R> function,
A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7) {
return acquire(PooledLambdaImpl.sPool,
- function, 7, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
+ function, 7, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, null,
+ null);
}
/**
@@ -1068,7 +1114,195 @@
? super G> function, A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7) {
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
- function, 7, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
+ function, 7, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, null,
+ null);
+ return Message.obtain().setCallback(callback.recycleOnUse());
+ }
+ }
+
+ /**
+ * {@link PooledRunnable} factory
+ *
+ * @param function non-capturing lambda(typically an unbounded method reference)
+ * to be invoked on call
+ * @param arg1 parameter supplied to {@code function} on call
+ * @param arg2 parameter supplied to {@code function} on call
+ * @param arg3 parameter supplied to {@code function} on call
+ * @param arg4 parameter supplied to {@code function} on call
+ * @param arg5 parameter supplied to {@code function} on call
+ * @param arg6 parameter supplied to {@code function} on call
+ * @param arg7 parameter supplied to {@code function} on call
+ * @param arg8 parameter supplied to {@code function} on call
+ * @return a {@link PooledRunnable}, equivalent to lambda:
+ * {@code () -> function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) }
+ */
+ static <A, B, C, D, E, F, G, H> PooledRunnable obtainRunnable(
+ OctConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F, ? super G,
+ ? super H> function, A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7,
+ H arg8) {
+ return acquire(PooledLambdaImpl.sPool,
+ function, 8, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
+ null);
+ }
+
+ /**
+ * {@link PooledSupplier} factory
+ *
+ * @param function non-capturing lambda(typically an unbounded method reference)
+ * to be invoked on call
+ * @param arg1 parameter supplied to {@code function} on call
+ * @param arg2 parameter supplied to {@code function} on call
+ * @param arg3 parameter supplied to {@code function} on call
+ * @param arg4 parameter supplied to {@code function} on call
+ * @param arg5 parameter supplied to {@code function} on call
+ * @param arg6 parameter supplied to {@code function} on call
+ * @param arg7 parameter supplied to {@code function} on call
+ * @param arg8 parameter supplied to {@code function} on call
+ * @return a {@link PooledSupplier}, equivalent to lambda:
+ * {@code () -> function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) }
+ */
+ static <A, B, C, D, E, F, G, H, R> PooledSupplier<R> obtainSupplier(
+ OctFunction<? super A, ? super B, ? super C, ? super D, ? super E, ? super F,
+ ? super G, ? super H, ? extends R> function,
+ A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7, H arg8) {
+ return acquire(PooledLambdaImpl.sPool,
+ function, 8, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
+ null);
+ }
+
+ /**
+ * Factory of {@link Message}s that contain an
+ * ({@link PooledLambda#recycleOnUse auto-recycling}) {@link PooledRunnable} as its
+ * {@link Message#getCallback internal callback}.
+ *
+ * The callback is equivalent to one obtainable via
+ * {@link #obtainRunnable(QuintConsumer, Object, Object, Object, Object, Object)}
+ *
+ * Note that using this method with {@link android.os.Handler#handleMessage}
+ * is more efficient than the alternative of {@link android.os.Handler#post}
+ * with a {@link PooledRunnable} due to the lack of 2 separate synchronization points
+ * when obtaining {@link Message} and {@link PooledRunnable} from pools separately
+ *
+ * You may optionally set a {@link Message#what} for the message if you want to be
+ * able to cancel it via {@link android.os.Handler#removeMessages}, but otherwise
+ * there's no need to do so
+ *
+ * @param function non-capturing lambda(typically an unbounded method reference)
+ * to be invoked on call
+ * @param arg1 parameter supplied to {@code function} on call
+ * @param arg2 parameter supplied to {@code function} on call
+ * @param arg3 parameter supplied to {@code function} on call
+ * @param arg4 parameter supplied to {@code function} on call
+ * @param arg5 parameter supplied to {@code function} on call
+ * @param arg6 parameter supplied to {@code function} on call
+ * @param arg7 parameter supplied to {@code function} on call
+ * @param arg8 parameter supplied to {@code function} on call
+ * @return a {@link Message} invoking {@code function(arg1, arg2, arg3, arg4, arg5, arg6,
+ * arg7, arg8) } when handled
+ */
+ static <A, B, C, D, E, F, G, H> Message obtainMessage(
+ OctConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F, ? super G,
+ ? super H> function, A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7,
+ H arg8) {
+ synchronized (Message.sPoolSync) {
+ PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
+ function, 8, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
+ null);
+ return Message.obtain().setCallback(callback.recycleOnUse());
+ }
+ }
+
+ /**
+ * {@link PooledRunnable} factory
+ *
+ * @param function non-capturing lambda(typically an unbounded method reference)
+ * to be invoked on call
+ * @param arg1 parameter supplied to {@code function} on call
+ * @param arg2 parameter supplied to {@code function} on call
+ * @param arg3 parameter supplied to {@code function} on call
+ * @param arg4 parameter supplied to {@code function} on call
+ * @param arg5 parameter supplied to {@code function} on call
+ * @param arg6 parameter supplied to {@code function} on call
+ * @param arg7 parameter supplied to {@code function} on call
+ * @param arg8 parameter supplied to {@code function} on call
+ * @param arg9 parameter supplied to {@code function} on call
+ * @return a {@link PooledRunnable}, equivalent to lambda:
+ * {@code () -> function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) }
+ */
+ static <A, B, C, D, E, F, G, H, I> PooledRunnable obtainRunnable(
+ NonaConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F,
+ ? super G, ? super H, ? super I> function, A arg1, B arg2, C arg3, D arg4,
+ E arg5, F arg6, G arg7, H arg8, I arg9) {
+ return acquire(PooledLambdaImpl.sPool,
+ function, 9, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
+ arg9);
+ }
+
+ /**
+ * {@link PooledSupplier} factory
+ *
+ * @param function non-capturing lambda(typically an unbounded method reference)
+ * to be invoked on call
+ * @param arg1 parameter supplied to {@code function} on call
+ * @param arg2 parameter supplied to {@code function} on call
+ * @param arg3 parameter supplied to {@code function} on call
+ * @param arg4 parameter supplied to {@code function} on call
+ * @param arg5 parameter supplied to {@code function} on call
+ * @param arg6 parameter supplied to {@code function} on call
+ * @param arg7 parameter supplied to {@code function} on call
+ * @param arg8 parameter supplied to {@code function} on call
+ * @param arg9 parameter supplied to {@code function} on call
+ * @return a {@link PooledSupplier}, equivalent to lambda:
+ * {@code () -> function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) }
+ */
+ static <A, B, C, D, E, F, G, H, I, R> PooledSupplier<R> obtainSupplier(
+ NonaFunction<? super A, ? super B, ? super C, ? super D, ? super E, ? super F,
+ ? super G, ? super H, ? super I, ? extends R> function,
+ A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7, H arg8, I arg9) {
+ return acquire(PooledLambdaImpl.sPool,
+ function, 9, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
+ arg9);
+ }
+
+ /**
+ * Factory of {@link Message}s that contain an
+ * ({@link PooledLambda#recycleOnUse auto-recycling}) {@link PooledRunnable} as its
+ * {@link Message#getCallback internal callback}.
+ *
+ * The callback is equivalent to one obtainable via
+ * {@link #obtainRunnable(QuintConsumer, Object, Object, Object, Object, Object)}
+ *
+ * Note that using this method with {@link android.os.Handler#handleMessage}
+ * is more efficient than the alternative of {@link android.os.Handler#post}
+ * with a {@link PooledRunnable} due to the lack of 2 separate synchronization points
+ * when obtaining {@link Message} and {@link PooledRunnable} from pools separately
+ *
+ * You may optionally set a {@link Message#what} for the message if you want to be
+ * able to cancel it via {@link android.os.Handler#removeMessages}, but otherwise
+ * there's no need to do so
+ *
+ * @param function non-capturing lambda(typically an unbounded method reference)
+ * to be invoked on call
+ * @param arg1 parameter supplied to {@code function} on call
+ * @param arg2 parameter supplied to {@code function} on call
+ * @param arg3 parameter supplied to {@code function} on call
+ * @param arg4 parameter supplied to {@code function} on call
+ * @param arg5 parameter supplied to {@code function} on call
+ * @param arg6 parameter supplied to {@code function} on call
+ * @param arg7 parameter supplied to {@code function} on call
+ * @param arg8 parameter supplied to {@code function} on call
+ * @param arg9 parameter supplied to {@code function} on call
+ * @return a {@link Message} invoking {@code function(arg1, arg2, arg3, arg4, arg5, arg6,
+ * arg7, arg8, arg9) } when handled
+ */
+ static <A, B, C, D, E, F, G, H, I> Message obtainMessage(
+ NonaConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F,
+ ? super G, ? super H, ? super I> function, A arg1, B arg2, C arg3, D arg4,
+ E arg5, F arg6, G arg7, H arg8, I arg9) {
+ synchronized (Message.sPoolSync) {
+ PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
+ function, 9, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
+ arg9);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
diff --git a/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java
index eea1e5f..6be626a 100755
--- a/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java
+++ b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java
@@ -30,6 +30,12 @@
import com.android.internal.util.function.HexConsumer;
import com.android.internal.util.function.HexFunction;
import com.android.internal.util.function.HexPredicate;
+import com.android.internal.util.function.NonaConsumer;
+import com.android.internal.util.function.NonaFunction;
+import com.android.internal.util.function.NonaPredicate;
+import com.android.internal.util.function.OctConsumer;
+import com.android.internal.util.function.OctFunction;
+import com.android.internal.util.function.OctPredicate;
import com.android.internal.util.function.QuadConsumer;
import com.android.internal.util.function.QuadFunction;
import com.android.internal.util.function.QuadPredicate;
@@ -54,12 +60,12 @@
* @hide
*/
final class PooledLambdaImpl<R> extends OmniFunction<Object,
- Object, Object, Object, Object, Object, Object, R> {
+ Object, Object, Object, Object, Object, Object, Object, Object, R> {
private static final boolean DEBUG = false;
private static final String LOG_TAG = "PooledLambdaImpl";
- private static final int MAX_ARGS = 7;
+ private static final int MAX_ARGS = 9;
private static final int MAX_POOL_SIZE = 50;
@@ -125,7 +131,7 @@
/**
* Bit schema:
- * AAAAAAABCDEEEEEEFFFFFF
+ * AAAAAAAAABCDEEEEEEFFFFFF
*
* Where:
* A - whether {@link #mArgs arg} at corresponding index was specified at
@@ -161,17 +167,19 @@
}
@Override
- R invoke(Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) {
+ R invoke(Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7,
+ Object a8, Object a9) {
checkNotRecycled();
if (DEBUG) {
Log.i(LOG_TAG, this + ".invoke("
+ commaSeparateFirstN(
- new Object[] { a1, a2, a3, a4, a5, a6, a7 },
+ new Object[] { a1, a2, a3, a4, a5, a6, a7, a8, a9 },
LambdaType.decodeArgCount(getFlags(MASK_EXPOSED_AS)))
+ ")");
}
- final boolean notUsed = fillInArg(a1) && fillInArg(a2) && fillInArg(a3)
- && fillInArg(a4) && fillInArg(a5) && fillInArg(a6) && fillInArg(a7);
+ final boolean notUsed = fillInArg(a1) && fillInArg(a2) && fillInArg(a3) && fillInArg(a4)
+ && fillInArg(a5) && fillInArg(a6) && fillInArg(a7) && fillInArg(a8)
+ && fillInArg(a9);
int argCount = LambdaType.decodeArgCount(getFlags(MASK_FUNC_TYPE));
if (argCount != LambdaType.MASK_ARG_COUNT) {
for (int i = 0; i < argCount; i++) {
@@ -335,7 +343,7 @@
popArg(2), popArg(3), popArg(4), popArg(5));
}
}
- }
+ } break;
case 7: {
switch (returnType) {
@@ -356,7 +364,49 @@
popArg(5), popArg(6));
}
}
- }
+ } break;
+
+ case 8: {
+ switch (returnType) {
+ case LambdaType.ReturnType.VOID: {
+ ((OctConsumer) mFunc).accept(popArg(0), popArg(1),
+ popArg(2), popArg(3), popArg(4),
+ popArg(5), popArg(6), popArg(7));
+ return null;
+ }
+ case LambdaType.ReturnType.BOOLEAN: {
+ return (R) (Object) ((OctPredicate) mFunc).test(popArg(0),
+ popArg(1), popArg(2), popArg(3),
+ popArg(4), popArg(5), popArg(6), popArg(7));
+ }
+ case LambdaType.ReturnType.OBJECT: {
+ return (R) ((OctFunction) mFunc).apply(popArg(0), popArg(1),
+ popArg(2), popArg(3), popArg(4),
+ popArg(5), popArg(6), popArg(7));
+ }
+ }
+ } break;
+
+ case 9: {
+ switch (returnType) {
+ case LambdaType.ReturnType.VOID: {
+ ((NonaConsumer) mFunc).accept(popArg(0), popArg(1),
+ popArg(2), popArg(3), popArg(4), popArg(5),
+ popArg(6), popArg(7), popArg(8));
+ return null;
+ }
+ case LambdaType.ReturnType.BOOLEAN: {
+ return (R) (Object) ((NonaPredicate) mFunc).test(popArg(0),
+ popArg(1), popArg(2), popArg(3), popArg(4),
+ popArg(5), popArg(6), popArg(7), popArg(8));
+ }
+ case LambdaType.ReturnType.OBJECT: {
+ return (R) ((NonaFunction) mFunc).apply(popArg(0), popArg(1),
+ popArg(2), popArg(3), popArg(4), popArg(5),
+ popArg(6), popArg(7), popArg(8));
+ }
+ }
+ } break;
}
throw new IllegalStateException("Unknown function type: " + LambdaType.toString(funcType));
}
@@ -419,8 +469,8 @@
* Internal non-typesafe factory method for {@link PooledLambdaImpl}
*/
static <E extends PooledLambda> E acquire(Pool pool, Object func,
- int fNumArgs, int numPlaceholders, int fReturnType,
- Object a, Object b, Object c, Object d, Object e, Object f, Object g) {
+ int fNumArgs, int numPlaceholders, int fReturnType, Object a, Object b, Object c,
+ Object d, Object e, Object f, Object g, Object h, Object i) {
PooledLambdaImpl r = acquire(pool);
if (DEBUG) {
Log.i(LOG_TAG,
@@ -436,6 +486,8 @@
+ ", e = " + e
+ ", f = " + f
+ ", g = " + g
+ + ", h = " + h
+ + ", i = " + i
+ ")");
}
r.mFunc = func;
@@ -449,6 +501,8 @@
setIfInBounds(r.mArgs, 4, e);
setIfInBounds(r.mArgs, 5, f);
setIfInBounds(r.mArgs, 6, g);
+ setIfInBounds(r.mArgs, 7, h);
+ setIfInBounds(r.mArgs, 8, i);
return (E) r;
}
@@ -474,13 +528,14 @@
}
@Override
- public OmniFunction<Object, Object, Object, Object, Object, Object, Object, R> negate() {
+ public OmniFunction<Object, Object, Object, Object, Object, Object, Object, Object, Object,
+ R> negate() {
throw new UnsupportedOperationException();
}
@Override
- public <V> OmniFunction<Object, Object, Object, Object, Object, Object, Object, V> andThen(
- Function<? super R, ? extends V> after) {
+ public <V> OmniFunction<Object, Object, Object, Object, Object, Object, Object, Object, Object,
+ V> andThen(Function<? super R, ? extends V> after) {
throw new UnsupportedOperationException();
}
@@ -500,7 +555,8 @@
}
@Override
- public OmniFunction<Object, Object, Object, Object, Object, Object, Object, R> recycleOnUse() {
+ public OmniFunction<Object, Object, Object, Object, Object, Object, Object, Object, Object,
+ R> recycleOnUse() {
if (DEBUG) Log.i(LOG_TAG, this + ".recycleOnUse()");
mFlags |= FLAG_RECYCLE_ON_USE;
return this;
@@ -584,6 +640,8 @@
case 5: return "Quint";
case 6: return "Hex";
case 7: return "Hept";
+ case 8: return "Oct";
+ case 9: return "Nona";
default: throw new IllegalArgumentException("" + argCount);
}
}
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 49d5007..fa1da4b 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -33,8 +33,6 @@
#include <iomanip>
#include <string>
-#include <android-base/stringprintf.h>
-#include <android-base/unique_fd.h>
#include <debuggerd/client.h>
#include <log/log.h>
#include <utils/misc.h>
@@ -50,10 +48,6 @@
namespace android
{
-static inline UniqueFile MakeUniqueFile(const char* path, const char* mode) {
- return UniqueFile(fopen(path, mode), safeFclose);
-}
-
enum {
HEAP_UNKNOWN,
HEAP_DALVIK,
diff --git a/core/jni/android_os_Debug.h b/core/jni/android_os_Debug.h
index 81270ca..c7b731b 100644
--- a/core/jni/android_os_Debug.h
+++ b/core/jni/android_os_Debug.h
@@ -19,6 +19,8 @@
#include <memory>
#include <stdio.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
namespace android {
@@ -27,6 +29,11 @@
}
using UniqueFile = std::unique_ptr<FILE, decltype(&safeFclose)>;
+
+inline UniqueFile MakeUniqueFile(const char* path, const char* mode) {
+ return UniqueFile(fopen(path, mode), safeFclose);
+}
+
UniqueFile OpenSmapsOrRollup(int pid);
} // namespace android
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 4c7defb..377e65c 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -1128,6 +1128,39 @@
return pss * 1024;
}
+static jlongArray android_os_Process_getRss(JNIEnv* env, jobject clazz, jint pid)
+{
+ // total, file, anon, swap
+ jlong rss[4] = {0, 0, 0, 0};
+ std::string status_path =
+ android::base::StringPrintf("/proc/%d/status", pid);
+ UniqueFile file = MakeUniqueFile(status_path.c_str(), "re");
+
+ char line[256];
+ while (fgets(line, sizeof(line), file.get())) {
+ jlong v;
+ if ( sscanf(line, "VmRSS: %" SCNd64 " kB", &v) == 1) {
+ rss[0] = v;
+ } else if ( sscanf(line, "RssFile: %" SCNd64 " kB", &v) == 1) {
+ rss[1] = v;
+ } else if ( sscanf(line, "RssAnon: %" SCNd64 " kB", &v) == 1) {
+ rss[2] = v;
+ } else if ( sscanf(line, "VmSwap: %" SCNd64 " kB", &v) == 1) {
+ rss[3] = v;
+ }
+ }
+
+ jlongArray rssArray = env->NewLongArray(4);
+ if (rssArray == NULL) {
+ jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
+ return NULL;
+ }
+
+ env->SetLongArrayRegion(rssArray, 0, 4, rss);
+
+ return rssArray;
+}
+
jintArray android_os_Process_getPidsForCommands(JNIEnv* env, jobject clazz,
jobjectArray commandNames)
{
@@ -1253,6 +1286,7 @@
{"parseProcLine", "([BII[I[Ljava/lang/String;[J[F)Z", (void*)android_os_Process_parseProcLine},
{"getElapsedCpuTime", "()J", (void*)android_os_Process_getElapsedCpuTime},
{"getPss", "(I)J", (void*)android_os_Process_getPss},
+ {"getRss", "(I)[J", (void*)android_os_Process_getRss},
{"getPidsForCommands", "([Ljava/lang/String;)[I", (void*)android_os_Process_getPidsForCommands},
//{"setApplicationObject", "(Landroid/os/IBinder;)V", (void*)android_os_Process_setApplicationObject},
{"killProcessGroup", "(II)I", (void*)android_os_Process_killProcessGroup},
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index e89b593..752624b 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -468,6 +468,10 @@
return reinterpret_cast<RenderNode*>(renderNodePtr)->stagingProperties().getAllowForceDark();
}
+static jlong android_view_RenderNode_getUniqueId(jlong renderNodePtr) {
+ return reinterpret_cast<RenderNode*>(renderNodePtr)->uniqueId();
+}
+
// ----------------------------------------------------------------------------
// RenderProperties - Animations
// ----------------------------------------------------------------------------
@@ -694,6 +698,7 @@
{ "nGetHeight", "(J)I", (void*) android_view_RenderNode_getHeight },
{ "nSetAllowForceDark", "(JZ)Z", (void*) android_view_RenderNode_setAllowForceDark },
{ "nGetAllowForceDark", "(J)Z", (void*) android_view_RenderNode_getAllowForceDark },
+ { "nGetUniqueId", "(J)J", (void*) android_view_RenderNode_getUniqueId },
};
int register_android_view_RenderNode(JNIEnv* env) {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 6c3085f..8b66be3 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4217,6 +4217,10 @@
@hide -->
<permission android:name="android.permission.RECEIVE_DEVICE_CUSTOMIZATION_READY"
android:protectionLevel="signature|preinstalled" />
+ <!-- @SystemApi Allows wallpaper to be rendered in ambient mode.
+ @hide -->
+ <permission android:name="android.permission.AMBIENT_WALLPAPER"
+ android:protectionLevel="signature|preinstalled" />
<application android:process="system"
android:persistent="true"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 470e1cc..918070c 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -7935,7 +7935,9 @@
wallpaper. -->
<attr name="showMetadataInPreview" format="boolean" />
- <!-- Wallpapers optimized and capable of drawing in ambient mode will return true. -->
+ <!-- Wallpapers optimized and capable of drawing in ambient mode will return true.
+ This feature requires the android.permission.AMBIENT_WALLPAPER permission.
+ @hide @SystemApi -->
<attr name="supportsAmbientMode" format="boolean" />
<!-- Uri that specifies a settings Slice for this wallpaper. -->
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 18d1d5d..089c59f 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1601,6 +1601,10 @@
<attr name="request" />
<attr name="protectionLevel" />
<attr name="permissionFlags" />
+ <!-- If {@code true} applications that target Q <em>must</em> specify the permission usage
+ attributes in their {@code uses-permission} elements or the permission will not be
+ granted. -->
+ <attr name="usageInfoRequired" format="boolean" />
</declare-styleable>
<!-- The <code>permission-group</code> tag declares a logical grouping of
@@ -1700,6 +1704,81 @@
requested. If it does support the feature, it will be as if the manifest didn't
request it at all. -->
<attr name="requiredNotFeature" format="string" />
+
+ <!-- Specify if the app uploads data, or derived data, guarded by this permission.
+
+ If the permission is defined with {@link android.R.attr#usageInfoRequired}
+ {@code true} this <em>must</em> be specified by apps that target Android Q or the
+ permission will not be granted, it will be as if the manifest didn't request it at all.
+ -->
+ <attr name="dataSentOffDevice">
+ <!-- The application may send data, or derived data, guarded by this permission off of the
+ device. -->
+ <enum name="yes" value="1" />
+ <!-- The application may send data, or derived data, guarded by this permission off of the
+ device, however it will only do so when explicitly triggered by a user action. -->
+ <enum name="userTriggered" value="2" />
+ <!-- The application does not send data, or derived data, guarded by this permission off
+ of the device. -->
+ <enum name="no" value="3" />
+ </attr>
+
+ <!-- Specify if the application or its related off-device services provide data,
+ or derived data, guarded by this permission to third parties outside of the developer's
+ organization that do not qualify as data processors.
+
+ If the permission is defined with {@link android.R.attr#usageInfoRequired}
+ {@code true} this <em>must</em> be specified by apps that target Android Q or the
+ permission will not be granted, it will be as if the manifest didn't request it at all.
+ -->
+ <attr name="dataSharedWithThirdParty">
+ <!-- The application or its services may provide data, or derived data, guarded by this
+ permission to third party organizations. -->
+ <enum name="yes" value="1" />
+ <!-- The application or its services may provide data, or derived data, guarded by this
+ permission to third party organizations, however it will only do so when explicitly
+ triggered by a user action. -->
+ <enum name="userTriggered" value="2" />
+ <!-- The application or its services does not provide data, or derived data, guarded by
+ this permission to third party organizations. -->
+ <enum name="no" value="3" />
+ </attr>
+
+ <!-- Specify if the application or its related off-device services use data,
+ or derived data, guarded by this permission for monetization purposes.
+
+ For example, if the data is sold to another party or used for targeting advertisements
+ this must be set to {@code yes}.
+
+ If the permission is defined with {@link android.R.attr#usageInfoRequired}
+ {@code true} this <em>must</em> be specified by apps that target Android Q or the
+ permission will not be granted, it will be as if the manifest didn't request it at all.
+ -->
+ <attr name="dataUsedForMonetization">
+ <!-- The application or its services may use data, or derived data, guarded by this
+ permission for monetization purposes. -->
+ <enum name="yes" value="1" />
+ <!-- The application or its services may use data, or derived data, guarded by this
+ permission for monetization purposes, however it will only do so when explicity
+ triggered by a user action. -->
+ <enum name="userTriggered" value="2" />
+ <!-- The application or its services does not use data, or derived data, guarded by
+ this permission for monetization purposes. -->
+ <enum name="no" value="3" />
+ </attr>
+
+ <!-- Specify how long the application or its related off-device services store
+ data, or derived data, guarded by this permission.
+
+ This can be one of "notRetained", "userSelected", "unlimited", or a number
+ representing the number of weeks the data is retained.
+
+ If the permission is defined with {@link android.R.attr#usageInfoRequired}
+ {@code true} this <em>must</em> be specified by apps that target Android Q or the
+ permission will not be granted, it will be as if the manifest didn't request it at all.
+ -->
+ <attr name="dataRetentionTime" format="string" />
+
</declare-styleable>
<!-- The <code>uses-configuration</code> tag specifies
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 63cac51..feefcad 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2909,6 +2909,7 @@
<public name="opticalInsetRight" />
<public name="opticalInsetBottom" />
<public name="forceDarkAllowed" />
+ <!-- @hide @SystemApi -->
<public name="supportsAmbientMode" />
<!-- @hide For use by platform and tools only. Developers should not specify this value. -->
<public name="usesNonSdkApi" />
@@ -2922,6 +2923,11 @@
<public name="importantForContentCapture" />
<public name="supportsMultipleDisplays" />
<public name="useAppZygote" />
+ <public name="usageInfoRequired" />
+ <public name="dataSentOffDevice" />
+ <public name="dataSharedWithThirdParty" />
+ <public name="dataUsedForMonetization" />
+ <public name="dataRetentionTime" />
</public-group>
<public-group type="drawable" first-id="0x010800b4">
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 3b0dc9d..135c137 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -64,7 +64,7 @@
public boolean isRecordingFor(Object o) { return false; }
// may be null
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 117521088)
private Bitmap mBitmap;
// optional field set by the caller
diff --git a/graphics/java/android/graphics/RenderNode.java b/graphics/java/android/graphics/RenderNode.java
index 45d7a21..d6f08b9 100644
--- a/graphics/java/android/graphics/RenderNode.java
+++ b/graphics/java/android/graphics/RenderNode.java
@@ -1173,6 +1173,22 @@
return nGetAllowForceDark(mNativeRenderNode);
}
+ /**
+ * Returns the unique ID that identifies this RenderNode. This ID is unique for the
+ * lifetime of the process. IDs are reset on process death, and are unique only within
+ * the process.
+ *
+ * This ID is intended to be used with debugging tools to associate a particular
+ * RenderNode across different debug dumping & inspection tools. For example
+ * a View layout inspector should include the unique ID for any RenderNodes that it owns
+ * to associate the drawing content with the layout content.
+ *
+ * @return the unique ID for this RenderNode
+ */
+ public long getUniqueId() {
+ return nGetUniqueId(mNativeRenderNode);
+ }
+
///////////////////////////////////////////////////////////////////////////
// Animations
///////////////////////////////////////////////////////////////////////////
@@ -1479,4 +1495,7 @@
@CriticalNative
private static native boolean nGetAllowForceDark(long renderNode);
+
+ @CriticalNative
+ private static native long nGetUniqueId(long renderNode);
}
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index caf610b..5bd59d4 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -713,11 +713,12 @@
}
/**
- * Whether this drawable requests projection.
+ * Whether this drawable requests projection. Indicates that the
+ * {@link android.graphics.RenderNode} this Drawable will draw into should be drawn immediately
+ * after the closest ancestor RenderNode containing a projection receiver.
*
- * @hide magic!
+ * @see android.graphics.RenderNode#setProjectBackwards(boolean)
*/
- @UnsupportedAppUsage
public boolean isProjected() {
return false;
}
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 4a5b61a..5f27fae 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -179,6 +179,7 @@
"renderthread/CanvasContext.cpp",
"renderthread/DrawFrameTask.cpp",
"renderthread/EglManager.cpp",
+ "renderthread/ReliableSurface.cpp",
"renderthread/VulkanManager.cpp",
"renderthread/RenderProxy.cpp",
"renderthread/RenderTask.cpp",
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index d2a8f02..4a63910 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -29,6 +29,7 @@
#include <SkPathOps.h>
#include <algorithm>
+#include <atomic>
#include <sstream>
#include <string>
@@ -47,8 +48,14 @@
TreeInfo* mTreeInfo;
};
+static int64_t generateId() {
+ static std::atomic<int64_t> sNextId{1};
+ return sNextId++;
+}
+
RenderNode::RenderNode()
- : mDirtyPropertyFields(0)
+ : mUniqueId(generateId())
+ , mDirtyPropertyFields(0)
, mNeedsDisplayListSync(false)
, mDisplayList(nullptr)
, mStagingDisplayList(nullptr)
@@ -444,5 +451,38 @@
return &mClippedOutlineCache.clippedOutline;
}
+using StringBuffer = FatVector<char, 128>;
+
+template <typename... T>
+static void format(StringBuffer& buffer, const std::string_view& format, T... args) {
+ buffer.resize(buffer.capacity());
+ while (1) {
+ int needed = snprintf(buffer.data(), buffer.size(),
+ format.data(), std::forward<T>(args)...);
+ if (needed < 0) {
+ buffer[0] = '\0';
+ buffer.resize(1);
+ return;
+ }
+ if (needed < buffer.size()) {
+ buffer.resize(needed + 1);
+ return;
+ }
+ buffer.resize(buffer.size() * 2);
+ }
+}
+
+void RenderNode::markDrawStart(SkCanvas& canvas) {
+ StringBuffer buffer;
+ format(buffer, "RenderNode(id=%d, name='%s')", uniqueId(), getName());
+ canvas.drawAnnotation(SkRect::MakeWH(getWidth(), getHeight()), buffer.data(), nullptr);
+}
+
+void RenderNode::markDrawEnd(SkCanvas& canvas) {
+ StringBuffer buffer;
+ format(buffer, "/RenderNode(id=%d, name='%s')", uniqueId(), getName());
+ canvas.drawAnnotation(SkRect::MakeWH(getWidth(), getHeight()), buffer.data(), nullptr);
+}
+
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index be0b46b..6060123 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -213,6 +213,11 @@
UsageHint usageHint() const { return mUsageHint; }
+ int64_t uniqueId() const { return mUniqueId; }
+
+ void markDrawStart(SkCanvas& canvas);
+ void markDrawEnd(SkCanvas& canvas);
+
private:
void computeOrderingImpl(RenderNodeOp* opState,
std::vector<RenderNodeOp*>* compositedChildrenOfProjectionSurface,
@@ -233,6 +238,7 @@
void incParentRefCount() { mParentCount++; }
void decParentRefCount(TreeObserver& observer, TreeInfo* info = nullptr);
+ const int64_t mUniqueId;
String8 mName;
sp<VirtualLightRefBase> mUserContext;
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index ea14d11..d80cb6d 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -115,12 +115,26 @@
}
}
+class MarkDraw {
+public:
+ explicit MarkDraw(SkCanvas& canvas, RenderNode& node) : mCanvas(canvas), mNode(node) {
+ if (CC_UNLIKELY(Properties::skpCaptureEnabled)) {
+ mNode.markDrawStart(mCanvas);
+ }
+ }
+ ~MarkDraw() {
+ if (CC_UNLIKELY(Properties::skpCaptureEnabled)) {
+ mNode.markDrawEnd(mCanvas);
+ }
+ }
+private:
+ SkCanvas& mCanvas;
+ RenderNode& mNode;
+};
+
void RenderNodeDrawable::forceDraw(SkCanvas* canvas) {
RenderNode* renderNode = mRenderNode.get();
- if (CC_UNLIKELY(Properties::skpCaptureEnabled)) {
- SkRect dimensions = SkRect::MakeWH(renderNode->getWidth(), renderNode->getHeight());
- canvas->drawAnnotation(dimensions, renderNode->getName(), nullptr);
- }
+ MarkDraw _marker{*canvas, *renderNode};
// We only respect the nothingToDraw check when we are composing a layer. This
// ensures that we paint the layer even if it is not currently visible in the
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
index 142bca9..07979a2 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -155,7 +155,7 @@
}
}
-bool SkiaOpenGLPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior,
+bool SkiaOpenGLPipeline::setSurface(ANativeWindow* surface, SwapBehavior swapBehavior,
ColorMode colorMode) {
if (mEglSurface != EGL_NO_SURFACE) {
mEglManager.destroySurface(mEglSurface);
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
index 4ab3541..47991069 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
@@ -42,7 +42,7 @@
bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty,
FrameInfo* currentFrameInfo, bool* requireSwap) override;
DeferredLayerUpdater* createTextureLayer() override;
- bool setSurface(Surface* window, renderthread::SwapBehavior swapBehavior,
+ bool setSurface(ANativeWindow* surface, renderthread::SwapBehavior swapBehavior,
renderthread::ColorMode colorMode) override;
void onStop() override;
bool isSurfaceReady() override;
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index a494e49..e50ad1c 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -115,7 +115,7 @@
void SkiaVulkanPipeline::onStop() {}
-bool SkiaVulkanPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior,
+bool SkiaVulkanPipeline::setSurface(ANativeWindow* surface, SwapBehavior swapBehavior,
ColorMode colorMode) {
if (mVkSurface) {
mVkManager.destroySurface(mVkSurface);
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
index 14c0d69..02874c7 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
@@ -38,7 +38,7 @@
bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty,
FrameInfo* currentFrameInfo, bool* requireSwap) override;
DeferredLayerUpdater* createTextureLayer() override;
- bool setSurface(Surface* window, renderthread::SwapBehavior swapBehavior,
+ bool setSurface(ANativeWindow* surface, renderthread::SwapBehavior swapBehavior,
renderthread::ColorMode colorMode) override;
void onStop() override;
bool isSurfaceReady() override;
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index f1a522e..182233f 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -142,7 +142,12 @@
void CanvasContext::setSurface(sp<Surface>&& surface) {
ATRACE_CALL();
- mNativeSurface = std::move(surface);
+ if (surface) {
+ mNativeSurface = new ReliableSurface{std::move(surface)};
+ mNativeSurface->setDequeueTimeout(500_ms);
+ } else {
+ mNativeSurface = nullptr;
+ }
ColorMode colorMode = mWideColorGamut ? ColorMode::WideColorGamut : ColorMode::SRGB;
bool hasSurface = mRenderPipeline->setSurface(mNativeSurface.get(), mSwapBehavior, colorMode);
@@ -285,6 +290,7 @@
info.damageAccumulator = &mDamageAccumulator;
info.layerUpdateQueue = &mLayerUpdateQueue;
+ info.out.canDrawThisFrame = true;
mAnimationContext->startFrame(info.mode);
mRenderPipeline->onPrepareTree();
@@ -304,7 +310,7 @@
mIsDirty = true;
- if (CC_UNLIKELY(!mNativeSurface.get())) {
+ if (CC_UNLIKELY(!hasSurface())) {
mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
info.out.canDrawThisFrame = false;
return;
@@ -323,27 +329,6 @@
// the deadline for RT animations
info.out.canDrawThisFrame = false;
}
- /* This logic exists to try and recover from a display latch miss, which essentially
- * results in the bufferqueue being double-buffered instead of triple-buffered.
- * SurfaceFlinger itself now tries to handle & recover from this situation, so this
- * logic should no longer be necessary. As it's occasionally triggering when
- * undesired disable it.
- * TODO: Remove this entirely if the results are solid.
- else if (vsyncDelta >= mRenderThread.timeLord().frameIntervalNanos() * 3 ||
- (latestVsync - mLastDropVsync) < 500_ms) {
- // It's been several frame intervals, assume the buffer queue is fine
- // or the last drop was too recent
- info.out.canDrawThisFrame = true;
- } else {
- info.out.canDrawThisFrame = !isSwapChainStuffed();
- if (!info.out.canDrawThisFrame) {
- // dropping frame
- mLastDropVsync = mRenderThread.timeLord().latestVsync();
- }
- }
- */
- } else {
- info.out.canDrawThisFrame = true;
}
// TODO: Do we need to abort out if the backdrop is added but not ready? Should that even
@@ -354,6 +339,19 @@
if (!info.out.canDrawThisFrame) {
mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
+ return;
+ }
+
+ int err = mNativeSurface->reserveNext();
+ if (err != OK) {
+ mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
+ info.out.canDrawThisFrame = false;
+ ALOGW("reserveNext failed, error = %d", err);
+ if (err != TIMED_OUT) {
+ // A timed out surface can still recover, but assume others are permanently dead.
+ setSurface(nullptr);
+ }
+ return;
}
bool postedFrameCallback = false;
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 70be4a6..9e7abf4 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -25,6 +25,7 @@
#include "IRenderPipeline.h"
#include "LayerUpdateQueue.h"
#include "RenderNode.h"
+#include "ReliableSurface.h"
#include "renderthread/RenderTask.h"
#include "renderthread/RenderThread.h"
#include "thread/Task.h"
@@ -219,7 +220,7 @@
EGLint mLastFrameHeight = 0;
RenderThread& mRenderThread;
- sp<Surface> mNativeSurface;
+ sp<ReliableSurface> mNativeSurface;
// stopped indicates the CanvasContext will reject actual redraw operations,
// and defer repaint until it is un-stopped
bool mStopped = false;
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index 65ced6a..8230dfd 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -31,6 +31,8 @@
#include <string>
#include <vector>
+#include <system/window.h>
+#include <gui/Surface.h>
#define GLES_VERSION 2
@@ -106,7 +108,7 @@
LOG_ALWAYS_FATAL_IF(eglInitialize(mEglDisplay, &major, &minor) == EGL_FALSE,
"Failed to initialize display %p! err=%s", mEglDisplay, eglErrorString());
- ALOGI("Initialized EGL, version %d.%d", (int)major, (int)minor);
+ ALOGV("Initialized EGL, version %d.%d", (int)major, (int)minor);
initExtensions();
diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h
index 4972554..42e17b273 100644
--- a/libs/hwui/renderthread/IRenderPipeline.h
+++ b/libs/hwui/renderthread/IRenderPipeline.h
@@ -28,9 +28,9 @@
class GrContext;
-namespace android {
+struct ANativeWindow;
-class Surface;
+namespace android {
namespace uirenderer {
@@ -67,7 +67,7 @@
virtual bool swapBuffers(const Frame& frame, bool drew, const SkRect& screenDirty,
FrameInfo* currentFrameInfo, bool* requireSwap) = 0;
virtual DeferredLayerUpdater* createTextureLayer() = 0;
- virtual bool setSurface(Surface* window, SwapBehavior swapBehavior, ColorMode colorMode) = 0;
+ virtual bool setSurface(ANativeWindow* window, SwapBehavior swapBehavior, ColorMode colorMode) = 0;
virtual void onStop() = 0;
virtual bool isSurfaceReady() = 0;
virtual bool isContextReady() = 0;
diff --git a/libs/hwui/renderthread/ReliableSurface.cpp b/libs/hwui/renderthread/ReliableSurface.cpp
new file mode 100644
index 0000000..0ab4cd2
--- /dev/null
+++ b/libs/hwui/renderthread/ReliableSurface.cpp
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ReliableSurface.h"
+
+#include <private/android/AHardwareBufferHelpers.h>
+
+namespace android::uirenderer::renderthread {
+
+// TODO: Make surface less protected
+// This exists because perform is a varargs, and ANativeWindow has no va_list perform.
+// So wrapping/chaining that is hard. Telling the compiler to ignore protected is easy, so we do
+// that instead
+struct SurfaceExposer : Surface {
+ // Make warnings happy
+ SurfaceExposer() = delete;
+
+ using Surface::setBufferCount;
+ using Surface::setSwapInterval;
+ using Surface::dequeueBuffer;
+ using Surface::queueBuffer;
+ using Surface::cancelBuffer;
+ using Surface::lockBuffer_DEPRECATED;
+ using Surface::perform;
+};
+
+#define callProtected(surface, func, ...) ((*surface).*&SurfaceExposer::func)(__VA_ARGS__)
+
+ReliableSurface::ReliableSurface(sp<Surface>&& surface) : mSurface(std::move(surface)) {
+ LOG_ALWAYS_FATAL_IF(!mSurface, "Error, unable to wrap a nullptr");
+
+ ANativeWindow::setSwapInterval = hook_setSwapInterval;
+ ANativeWindow::dequeueBuffer = hook_dequeueBuffer;
+ ANativeWindow::cancelBuffer = hook_cancelBuffer;
+ ANativeWindow::queueBuffer = hook_queueBuffer;
+ ANativeWindow::query = hook_query;
+ ANativeWindow::perform = hook_perform;
+
+ ANativeWindow::dequeueBuffer_DEPRECATED = hook_dequeueBuffer_DEPRECATED;
+ ANativeWindow::cancelBuffer_DEPRECATED = hook_cancelBuffer_DEPRECATED;
+ ANativeWindow::lockBuffer_DEPRECATED = hook_lockBuffer_DEPRECATED;
+ ANativeWindow::queueBuffer_DEPRECATED = hook_queueBuffer_DEPRECATED;
+}
+
+void ReliableSurface::perform(int operation, va_list args) {
+ std::lock_guard _lock{mMutex};
+
+ switch (operation) {
+ case NATIVE_WINDOW_SET_USAGE:
+ mUsage = va_arg(args, uint32_t);
+ break;
+ case NATIVE_WINDOW_SET_USAGE64:
+ mUsage = va_arg(args, uint64_t);
+ break;
+ case NATIVE_WINDOW_SET_BUFFERS_GEOMETRY:
+ /* width */ va_arg(args, uint32_t);
+ /* height */ va_arg(args, uint32_t);
+ mFormat = va_arg(args, PixelFormat);
+ break;
+ case NATIVE_WINDOW_SET_BUFFERS_FORMAT:
+ mFormat = va_arg(args, PixelFormat);
+ break;
+ }
+}
+
+int ReliableSurface::reserveNext() {
+ {
+ std::lock_guard _lock{mMutex};
+ if (mReservedBuffer) {
+ ALOGW("reserveNext called but there was already a buffer reserved?");
+ return OK;
+ }
+ if (mInErrorState) {
+ return UNKNOWN_ERROR;
+ }
+ }
+
+ int fenceFd = -1;
+ ANativeWindowBuffer* buffer = nullptr;
+ int result = callProtected(mSurface, dequeueBuffer, &buffer, &fenceFd);
+
+ {
+ std::lock_guard _lock{mMutex};
+ LOG_ALWAYS_FATAL_IF(mReservedBuffer, "race condition in reserveNext");
+ mReservedBuffer = buffer;
+ mReservedFenceFd.reset(fenceFd);
+ if (result != OK) {
+ ALOGW("reserveNext failed, error %d", result);
+ }
+ }
+
+ return result;
+}
+
+void ReliableSurface::clearReservedBuffer() {
+ std::lock_guard _lock{mMutex};
+ if (mReservedBuffer) {
+ ALOGW("Reserved buffer %p was never used", mReservedBuffer);
+ }
+ mReservedBuffer = nullptr;
+ mReservedFenceFd.reset();
+}
+
+int ReliableSurface::cancelBuffer(ANativeWindowBuffer* buffer, int fenceFd) {
+ clearReservedBuffer();
+ if (isFallbackBuffer(buffer)) {
+ if (fenceFd > 0) {
+ close(fenceFd);
+ }
+ return OK;
+ }
+ int result = callProtected(mSurface, cancelBuffer, buffer, fenceFd);
+ return result;
+}
+
+int ReliableSurface::dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd) {
+ {
+ std::lock_guard _lock{mMutex};
+ if (mReservedBuffer) {
+ *buffer = mReservedBuffer;
+ *fenceFd = mReservedFenceFd.release();
+ mReservedBuffer = nullptr;
+ return OK;
+ }
+ }
+
+ int result = callProtected(mSurface, dequeueBuffer, buffer, fenceFd);
+ if (result != OK) {
+ ALOGW("dequeueBuffer failed, error = %d; switching to fallback", result);
+ *buffer = acquireFallbackBuffer();
+ *fenceFd = -1;
+ return *buffer ? OK : INVALID_OPERATION;
+ }
+ return OK;
+}
+
+int ReliableSurface::queueBuffer(ANativeWindowBuffer* buffer, int fenceFd) {
+ clearReservedBuffer();
+
+ if (isFallbackBuffer(buffer)) {
+ if (fenceFd > 0) {
+ close(fenceFd);
+ }
+ return OK;
+ }
+
+ int result = callProtected(mSurface, queueBuffer, buffer, fenceFd);
+ return result;
+}
+
+bool ReliableSurface::isFallbackBuffer(const ANativeWindowBuffer* windowBuffer) const {
+ if (!mScratchBuffer || !windowBuffer) {
+ return false;
+ }
+ ANativeWindowBuffer* scratchBuffer =
+ AHardwareBuffer_to_ANativeWindowBuffer(mScratchBuffer.get());
+ return windowBuffer == scratchBuffer;
+}
+
+ANativeWindowBuffer* ReliableSurface::acquireFallbackBuffer() {
+ std::lock_guard _lock{mMutex};
+ mInErrorState = true;
+
+ if (mScratchBuffer) {
+ return AHardwareBuffer_to_ANativeWindowBuffer(mScratchBuffer.get());
+ }
+
+ AHardwareBuffer_Desc desc;
+ desc.usage = mUsage;
+ desc.format = mFormat;
+ desc.width = 1;
+ desc.height = 1;
+ desc.layers = 1;
+ desc.rfu0 = 0;
+ desc.rfu1 = 0;
+ AHardwareBuffer* newBuffer = nullptr;
+ int err = AHardwareBuffer_allocate(&desc, &newBuffer);
+ if (err) {
+ // Allocate failed, that sucks
+ ALOGW("Failed to allocate scratch buffer, error=%d", err);
+ return nullptr;
+ }
+ mScratchBuffer.reset(newBuffer);
+ return AHardwareBuffer_to_ANativeWindowBuffer(newBuffer);
+}
+
+Surface* ReliableSurface::getWrapped(const ANativeWindow* window) {
+ return getSelf(window)->mSurface.get();
+}
+
+int ReliableSurface::hook_setSwapInterval(ANativeWindow* window, int interval) {
+ return callProtected(getWrapped(window), setSwapInterval, interval);
+}
+
+int ReliableSurface::hook_dequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer,
+ int* fenceFd) {
+ return getSelf(window)->dequeueBuffer(buffer, fenceFd);
+}
+
+int ReliableSurface::hook_cancelBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer,
+ int fenceFd) {
+ return getSelf(window)->cancelBuffer(buffer, fenceFd);
+}
+
+int ReliableSurface::hook_queueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer,
+ int fenceFd) {
+ return getSelf(window)->queueBuffer(buffer, fenceFd);
+}
+
+int ReliableSurface::hook_dequeueBuffer_DEPRECATED(ANativeWindow* window,
+ ANativeWindowBuffer** buffer) {
+ ANativeWindowBuffer* buf;
+ int fenceFd = -1;
+ int result = window->dequeueBuffer(window, &buf, &fenceFd);
+ if (result != OK) {
+ return result;
+ }
+ sp<Fence> fence(new Fence(fenceFd));
+ int waitResult = fence->waitForever("dequeueBuffer_DEPRECATED");
+ if (waitResult != OK) {
+ ALOGE("dequeueBuffer_DEPRECATED: Fence::wait returned an error: %d", waitResult);
+ window->cancelBuffer(window, buf, -1);
+ return waitResult;
+ }
+ *buffer = buf;
+ return result;
+}
+
+int ReliableSurface::hook_cancelBuffer_DEPRECATED(ANativeWindow* window,
+ ANativeWindowBuffer* buffer) {
+ return window->cancelBuffer(window, buffer, -1);
+}
+
+int ReliableSurface::hook_lockBuffer_DEPRECATED(ANativeWindow* window,
+ ANativeWindowBuffer* buffer) {
+ // This method is a no-op in Surface as well
+ return OK;
+}
+
+int ReliableSurface::hook_queueBuffer_DEPRECATED(ANativeWindow* window,
+ ANativeWindowBuffer* buffer) {
+ return window->queueBuffer(window, buffer, -1);
+}
+
+int ReliableSurface::hook_query(const ANativeWindow* window, int what, int* value) {
+ return getWrapped(window)->query(what, value);
+}
+
+int ReliableSurface::hook_perform(ANativeWindow* window, int operation, ...) {
+ va_list args;
+ va_start(args, operation);
+ int result = callProtected(getWrapped(window), perform, operation, args);
+ va_end(args);
+
+ switch (operation) {
+ case NATIVE_WINDOW_SET_BUFFERS_FORMAT:
+ case NATIVE_WINDOW_SET_USAGE:
+ case NATIVE_WINDOW_SET_USAGE64:
+ va_start(args, operation);
+ getSelf(window)->perform(operation, args);
+ va_end(args);
+ break;
+ default:
+ break;
+ }
+
+ return result;
+}
+
+}; // namespace android::uirenderer::renderthread
\ No newline at end of file
diff --git a/libs/hwui/renderthread/ReliableSurface.h b/libs/hwui/renderthread/ReliableSurface.h
new file mode 100644
index 0000000..9ae53a9
--- /dev/null
+++ b/libs/hwui/renderthread/ReliableSurface.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gui/Surface.h>
+#include <utils/Macros.h>
+#include <utils/StrongPointer.h>
+
+#include <memory>
+
+namespace android::uirenderer::renderthread {
+
+class ReliableSurface : public ANativeObjectBase<ANativeWindow, ReliableSurface, RefBase> {
+ PREVENT_COPY_AND_ASSIGN(ReliableSurface);
+
+public:
+ ReliableSurface(sp<Surface>&& surface);
+
+ void setDequeueTimeout(nsecs_t timeout) { mSurface->setDequeueTimeout(timeout); }
+
+ int reserveNext();
+
+ void allocateBuffers() { mSurface->allocateBuffers(); }
+
+ int query(int what, int* value) const { return mSurface->query(what, value); }
+
+ nsecs_t getLastDequeueStartTime() const { return mSurface->getLastDequeueStartTime(); }
+
+ uint64_t getNextFrameNumber() const { return mSurface->getNextFrameNumber(); }
+
+private:
+ const sp<Surface> mSurface;
+
+ mutable std::mutex mMutex;
+
+ uint64_t mUsage = AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER;
+ PixelFormat mFormat = PIXEL_FORMAT_RGBA_8888;
+ std::unique_ptr<AHardwareBuffer, void (*)(AHardwareBuffer*)> mScratchBuffer{
+ nullptr, AHardwareBuffer_release};
+ bool mInErrorState = false;
+ ANativeWindowBuffer* mReservedBuffer = nullptr;
+ base::unique_fd mReservedFenceFd;
+
+ bool isFallbackBuffer(const ANativeWindowBuffer* windowBuffer) const;
+ ANativeWindowBuffer* acquireFallbackBuffer();
+ void clearReservedBuffer();
+
+ void perform(int operation, va_list args);
+ int cancelBuffer(ANativeWindowBuffer* buffer, int fenceFd);
+ int dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd);
+ int queueBuffer(ANativeWindowBuffer* buffer, int fenceFd);
+
+ static Surface* getWrapped(const ANativeWindow*);
+
+ // ANativeWindow hooks
+ static int hook_cancelBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd);
+ static int hook_dequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer,
+ int* fenceFd);
+ static int hook_queueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd);
+
+ static int hook_perform(ANativeWindow* window, int operation, ...);
+ static int hook_query(const ANativeWindow* window, int what, int* value);
+ static int hook_setSwapInterval(ANativeWindow* window, int interval);
+
+ static int hook_cancelBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer* buffer);
+ static int hook_dequeueBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer** buffer);
+ static int hook_lockBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer* buffer);
+ static int hook_queueBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer* buffer);
+};
+
+}; // namespace android::uirenderer::renderthread
\ No newline at end of file
diff --git a/media/java/android/media/midi/MidiOutputPort.java b/media/java/android/media/midi/MidiOutputPort.java
index 511f6cd..5411e66 100644
--- a/media/java/android/media/midi/MidiOutputPort.java
+++ b/media/java/android/media/midi/MidiOutputPort.java
@@ -59,6 +59,8 @@
// read next event
int count = mInputStream.read(buffer);
if (count < 0) {
+ // This is the exit condition as read() returning <0 indicates
+ // that the pipe has been closed.
break;
// FIXME - inform receivers here?
}
@@ -81,10 +83,15 @@
Log.e(TAG, "Unknown packet type " + packetType);
break;
}
- }
+ } // while (true)
} catch (IOException e) {
// FIXME report I/O failure?
- Log.e(TAG, "read failed", e);
+ // TODO: The comment above about the exit condition is not currently working
+ // as intended. The read from the closed pipe is throwing an error rather than
+ // returning <0, so this becomes (probably) not an error, but the exit case.
+ // This warrants further investigation;
+ // Silence the (probably) spurious error message.
+ // Log.e(TAG, "read failed", e);
} finally {
IoUtils.closeQuietly(mInputStream);
}
diff --git a/packages/SettingsLib/SettingsLayoutPreference/res/layout/settings_entity_header.xml b/packages/SettingsLib/SettingsLayoutPreference/res/layout/settings_entity_header.xml
index 0678263..789d185 100644
--- a/packages/SettingsLib/SettingsLayoutPreference/res/layout/settings_entity_header.xml
+++ b/packages/SettingsLib/SettingsLayoutPreference/res/layout/settings_entity_header.xml
@@ -72,7 +72,6 @@
</LinearLayout>
<LinearLayout
- android:id="@+id/entity_header_links"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_centerVertical="true"
@@ -85,6 +84,7 @@
android:layout_width="wrap_content"
android:layout_weight="1"
android:layout_height="0dp"
+ android:visibility="gone"
android:minWidth="24dp"
android:src="@null"
android:tint="?android:attr/colorAccent"/>
@@ -95,6 +95,7 @@
android:layout_width="wrap_content"
android:layout_weight="1"
android:layout_height="0dp"
+ android:visibility="gone"
android:minWidth="24dp"
android:src="@null"
android:tint="?android:attr/colorAccent"/>
diff --git a/packages/SettingsLib/tests/robotests/Android.mk b/packages/SettingsLib/tests/robotests/Android.mk
index d15a3ef..cfa067f 100644
--- a/packages/SettingsLib/tests/robotests/Android.mk
+++ b/packages/SettingsLib/tests/robotests/Android.mk
@@ -32,12 +32,13 @@
include $(BUILD_PACKAGE)
-#############################################
-# SettingsLib Robolectric test target. #
-#############################################
+############################################################
+# SettingsLib Robolectric test target. #
+############################################################
include $(CLEAR_VARS)
LOCAL_MODULE := SettingsLibRoboTests
+LOCAL_MODULE_CLASS := JAVA_LIBRARIES
LOCAL_SRC_FILES := $(call all-java-files-under, src)
@@ -53,6 +54,9 @@
LOCAL_MODULE_TAGS := optional
+# Generate test_config.properties
+include external/robolectric-shadows/gen_test_config.mk
+
include $(BUILD_STATIC_JAVA_LIBRARY)
#############################################################
diff --git a/packages/SettingsLib/tests/robotests/config/robolectric.properties b/packages/SettingsLib/tests/robotests/config/robolectric.properties
index 6b5b8e5..fab7251 100644
--- a/packages/SettingsLib/tests/robotests/config/robolectric.properties
+++ b/packages/SettingsLib/tests/robotests/config/robolectric.properties
@@ -1,5 +1 @@
-manifest=frameworks/base/packages/SettingsLib/tests/robotests/AndroidManifest.xml
sdk=NEWEST_SDK
-
-shadows=\
- com.android.settingslib.testutils.shadow.ShadowXmlUtils
\ No newline at end of file
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/CustomEditTextPreferenceComaptTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/CustomEditTextPreferenceComaptTest.java
index 9ba9967..3a4e2e4 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/CustomEditTextPreferenceComaptTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/CustomEditTextPreferenceComaptTest.java
@@ -30,10 +30,11 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.util.ReflectionHelpers;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class CustomEditTextPreferenceComaptTest {
@Mock
@@ -70,7 +71,7 @@
}
private static class TestPreference extends CustomEditTextPreferenceCompat {
- public TestPreference(Context context) {
+ private TestPreference(Context context) {
super(context);
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/CustomEditTextPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/CustomEditTextPreferenceTest.java
index 9d7f59a..e94a06c 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/CustomEditTextPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/CustomEditTextPreferenceTest.java
@@ -30,10 +30,11 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.util.ReflectionHelpers;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class CustomEditTextPreferenceTest {
@Mock
@@ -70,7 +71,7 @@
}
private static class TestPreference extends CustomEditTextPreference {
- public TestPreference(Context context) {
+ private TestPreference(Context context) {
super(context);
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/DeviceInfoUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/DeviceInfoUtilsTest.java
index 19a916c..4e8af73 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/DeviceInfoUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/DeviceInfoUtilsTest.java
@@ -24,9 +24,10 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class DeviceInfoUtilsTest {
private Context mContext;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/HelpUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/HelpUtilsTest.java
index 36b70df..4d76331 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/HelpUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/HelpUtilsTest.java
@@ -18,12 +18,13 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.R;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
@@ -36,20 +37,19 @@
import android.provider.Settings;
import android.view.MenuItem;
-import android.R;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
/**
* Tests for {@link HelpUtils}.
*/
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class HelpUtilsTest {
private static final String TEST_HELP_URL = "intent:#Intent;action=com.android.test;end";
private static final String PACKAGE_NAME_KEY = "package-name-key";
@@ -83,8 +83,6 @@
when(mContext.getResources().getString(R.string.config_feedbackIntentNameKey))
.thenReturn(FEEDBACK_INTENT_NAME_KEY);
when(mActivity.getPackageManager()).thenReturn(mPackageManager);
-
-
}
@Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java
index 88ac8ce..2b5a4e0 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java
@@ -25,8 +25,8 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.when;
@@ -44,11 +44,12 @@
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import java.util.Arrays;
import java.util.Collections;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class RestrictedLockUtilsTest {
@Mock
@@ -178,8 +179,7 @@
public void checkIfKeyguardFeaturesAreDisabled_doesMatchAllowedFeature_unifiedManagedProfile() {
UserInfo userInfo = setUpUser(mUserId, new ComponentName[] {mAdmin1});
UserInfo profileInfo = setUpManagedProfile(mProfileId, new ComponentName[] {mAdmin2});
- when(mUserManager.getProfiles(mUserId)).thenReturn(Arrays.asList(new UserInfo[] {
- userInfo, profileInfo}));
+ when(mUserManager.getProfiles(mUserId)).thenReturn(Arrays.asList(userInfo, profileInfo));
when(mDevicePolicyManager.getKeyguardDisabledFeatures(mAdmin1, mUserId))
.thenReturn(KEYGUARD_DISABLE_FEATURES_NONE);
@@ -207,8 +207,7 @@
public void checkIfKeyguardFeaturesAreDisabled_notMatchOtherFeatures_unifiedManagedProfile() {
UserInfo userInfo = setUpUser(mUserId, new ComponentName[] {mAdmin1});
UserInfo profileInfo = setUpManagedProfile(mProfileId, new ComponentName[] {mAdmin2});
- when(mUserManager.getProfiles(mUserId)).thenReturn(Arrays.asList(new UserInfo[] {
- userInfo, profileInfo}));
+ when(mUserManager.getProfiles(mUserId)).thenReturn(Arrays.asList(userInfo, profileInfo));
when(mDevicePolicyManager.getKeyguardDisabledFeatures(mAdmin1, mUserId))
.thenReturn(KEYGUARD_DISABLE_FEATURES_NONE);
@@ -231,8 +230,7 @@
public void checkIfKeyguardFeaturesAreDisabled_onlyMatchesProfile_separateManagedProfile() {
UserInfo userInfo = setUpUser(mUserId, new ComponentName[] {mAdmin1});
UserInfo profileInfo = setUpManagedProfile(mProfileId, new ComponentName[] {mAdmin2});
- when(mUserManager.getProfiles(mUserId)).thenReturn(Arrays.asList(new UserInfo[] {
- userInfo, profileInfo}));
+ when(mUserManager.getProfiles(mUserId)).thenReturn(Arrays.asList(userInfo, profileInfo));
when(mDevicePolicyManager.getKeyguardDisabledFeatures(mAdmin1, mUserId))
.thenReturn(KEYGUARD_DISABLE_FEATURES_NONE);
@@ -268,8 +266,7 @@
public void checkIfKeyguardFeaturesAreDisabled_onlyMatchesParent_profileParentPolicy() {
UserInfo userInfo = setUpUser(mUserId, new ComponentName[] {mAdmin1});
UserInfo profileInfo = setUpManagedProfile(mProfileId, new ComponentName[] {mAdmin2});
- when(mUserManager.getProfiles(mUserId)).thenReturn(Arrays.asList(new UserInfo[] {
- userInfo, profileInfo}));
+ when(mUserManager.getProfiles(mUserId)).thenReturn(Arrays.asList(userInfo, profileInfo));
when(mProxy.getParentProfileInstance(any(DevicePolicyManager.class), any())
.getKeyguardDisabledFeatures(mAdmin2, mProfileId))
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedPreferenceHelperTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedPreferenceHelperTest.java
index 79d682d6..1b10c73 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedPreferenceHelperTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedPreferenceHelperTest.java
@@ -16,7 +16,6 @@
package com.android.settingslib;
-
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -35,8 +34,9 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class RestrictedPreferenceHelperTest {
@Mock
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java
deleted file mode 100644
index 8757eed..0000000
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.settingslib;
-
-import android.annotation.NonNull;
-
-import org.junit.runners.model.InitializationError;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.Config;
-import org.robolectric.manifest.AndroidManifest;
-import org.robolectric.res.Fs;
-import org.robolectric.res.ResourcePath;
-
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.List;
-
-public class SettingsLibRobolectricTestRunner extends RobolectricTestRunner {
-
- public SettingsLibRobolectricTestRunner(Class<?> testClass) throws InitializationError {
- super(testClass);
- }
-
- /**
- * We are going to create our own custom manifest so we can add multiple resource paths to it.
- */
- @Override
- protected AndroidManifest getAppManifest(Config config) {
- try {
- // Using the manifest file's relative path, we can figure out the application directory.
- final URL appRoot =
- new URL("file:frameworks/base/packages/SettingsLib/tests/robotests");
- final URL manifestPath = new URL(appRoot, "AndroidManifest.xml");
- final URL resDir = new URL(appRoot, "res");
- final URL assetsDir = new URL(appRoot, "assets");
-
- return new AndroidManifest(Fs.fromURL(manifestPath), Fs.fromURL(resDir),
- Fs.fromURL(assetsDir), "com.android.settingslib") {
- @Override
- public List<ResourcePath> getIncludedResourcePaths() {
- final List<ResourcePath> paths = super.getIncludedResourcePaths();
- paths.add(resourcePath("file:frameworks/base/packages/SettingsLib/AppPreference/res"));
- paths.add(resourcePath("file:frameworks/base/packages/SettingsLib/HelpUtils/res"));
- paths.add(resourcePath("file:frameworks/base/packages/SettingsLib/RestrictedLockUtils/res"));
- paths.add(resourcePath("file:frameworks/base/packages/SettingsLib/"
- + "SettingsLayoutPreference/res"));
- paths.add(resourcePath("file:frameworks/base/packages/SettingsLib/res"));
- paths.add(resourcePath("file:frameworks/base/core/res/res"));
- paths.add(resourcePath("file:out/soong/.intermediates/prebuilts/sdk/current/androidx/androidx.appcompat_appcompat-nodeps/android_common/aar/res/"));
- return paths;
- }
- };
- } catch (MalformedURLException e) {
- throw new RuntimeException("SettingsLibRobolectricTestRunner failure", e);
- }
- }
-
- private static ResourcePath resourcePath(@NonNull String spec) {
- try {
- return new ResourcePath(null, Fs.fromURL(new URL(spec)), null);
- } catch (MalformedURLException e) {
- throw new RuntimeException("SettingsLibRobolectricTestRunner failure", e);
- }
- }
-}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TetherUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TetherUtilTest.java
index e70baa1..0ca7791 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TetherUtilTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TetherUtilTest.java
@@ -32,12 +32,13 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList;
import java.util.List;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class TetherUtilTest {
private Context mContext;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TwoTargetPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TwoTargetPreferenceTest.java
index c0b69f2..3f0ba13 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TwoTargetPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TwoTargetPreferenceTest.java
@@ -36,9 +36,10 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class TwoTargetPreferenceTest {
private PreferenceViewHolder mViewHolder;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
index 08a75ab..594d767 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
@@ -49,6 +49,7 @@
import org.mockito.ArgumentMatchers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.annotation.Implementation;
@@ -58,10 +59,8 @@
import java.util.HashMap;
import java.util.Map;
-@RunWith(SettingsLibRobolectricTestRunner.class)
-@Config(shadows = {
- UtilsTest.ShadowSecure.class,
- UtilsTest.ShadowLocationManager.class})
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {UtilsTest.ShadowSecure.class, UtilsTest.ShadowLocationManager.class})
public class UtilsTest {
private static final double[] TEST_PERCENTAGES = {0, 0.4, 0.5, 0.6, 49, 49.3, 49.8, 50, 100};
private static final String PERCENTAGE_0 = "0%";
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/accessibility/AccessibilityUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/accessibility/AccessibilityUtilsTest.java
index 152d024..44fdaec 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/accessibility/AccessibilityUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/accessibility/AccessibilityUtilsTest.java
@@ -23,14 +23,13 @@
import android.os.UserHandle;
import android.provider.Settings;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class AccessibilityUtilsTest {
private Context mContext;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
index b307b47..ccec175a 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
@@ -41,7 +41,6 @@
import android.os.UserHandle;
import android.util.IconDrawableFactory;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
import com.android.settingslib.applications.ApplicationsState.Callbacks;
import com.android.settingslib.applications.ApplicationsState.Session;
@@ -55,6 +54,7 @@
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.annotation.Implementation;
@@ -67,7 +67,7 @@
import java.util.List;
import java.util.UUID;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowUserManager.class,
ApplicationsStateRoboTest.ShadowIconDrawableFactory.class,
ApplicationsStateRoboTest.ShadowPackageManager.class})
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/DefaultAppInfoTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/DefaultAppInfoTest.java
index a92a2dd..50fad70 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/DefaultAppInfoTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/DefaultAppInfoTest.java
@@ -18,8 +18,8 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@@ -32,16 +32,15 @@
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class DefaultAppInfoTest {
@Mock
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ServiceListingTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ServiceListingTest.java
index d8c459c..f7fd25b 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ServiceListingTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ServiceListingTest.java
@@ -26,14 +26,13 @@
import android.content.ComponentName;
import android.provider.Settings;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class ServiceListingTest {
private static final String TEST_SETTING = "testSetting";
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
index 29831a8..c555cbe 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
@@ -17,8 +17,8 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -32,7 +32,6 @@
import android.content.res.Resources;
import com.android.settingslib.R;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
import org.junit.Before;
@@ -40,26 +39,27 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowBluetoothAdapter.class})
public class A2dpProfileTest {
@Mock
- Context mContext;
+ private Context mContext;
@Mock
- CachedBluetoothDeviceManager mDeviceManager;
+ private CachedBluetoothDeviceManager mDeviceManager;
@Mock
- LocalBluetoothProfileManager mProfileManager;
+ private LocalBluetoothProfileManager mProfileManager;
@Mock
- BluetoothDevice mDevice;
+ private BluetoothDevice mDevice;
@Mock
- BluetoothA2dp mBluetoothA2dp;
- BluetoothProfile.ServiceListener mServiceListener;
+ private BluetoothA2dp mBluetoothA2dp;
+ private BluetoothProfile.ServiceListener mServiceListener;
- A2dpProfile mProfile;
+ private A2dpProfile mProfile;
private ShadowBluetoothAdapter mShadowBluetoothAdapter;
@Before
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpSinkProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpSinkProfileTest.java
index 274fff8..976445e 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpSinkProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpSinkProfileTest.java
@@ -18,18 +18,14 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.when;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import android.bluetooth.BluetoothA2dpSink;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothA2dpSink;
import android.bluetooth.BluetoothProfile;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
import org.junit.Before;
@@ -37,11 +33,12 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.robolectric.annotation.Config;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowBluetoothAdapter.class})
public class A2dpSinkProfileTest {
@@ -52,8 +49,6 @@
@Mock
private BluetoothA2dpSink mService;
@Mock
- private CachedBluetoothDevice mCachedBluetoothDevice;
- @Mock
private BluetoothDevice mBluetoothDevice;
private BluetoothProfile.ServiceListener mServiceListener;
private A2dpSinkProfile mProfile;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
index c147d5e..27b8dfc 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
@@ -29,20 +29,18 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.os.Handler;
import android.os.UserHandle;
import android.telephony.TelephonyManager;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class BluetoothEventManagerTest {
@Mock
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
index 07310bd..0eb6de9 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
@@ -21,14 +21,14 @@
import android.graphics.drawable.Drawable;
import com.android.settingslib.R;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.graph.BluetoothDeviceLayerDrawable;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class BluetoothUtilsTest {
@Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
index 9c75491..47b1210 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
@@ -28,18 +28,17 @@
import android.bluetooth.BluetoothProfile;
import android.content.Context;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.Collection;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class CachedBluetoothDeviceManagerTest {
private final static String DEVICE_NAME_1 = "TestName_1";
private final static String DEVICE_NAME_2 = "TestName_2";
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index 5ceede1..4e5d38a 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -31,16 +31,15 @@
import android.content.Context;
import android.media.AudioManager;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class CachedBluetoothDeviceTest {
private final static String DEVICE_NAME = "TestName";
private final static String DEVICE_ALIAS = "TestAlias";
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HeadsetProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HeadsetProfileTest.java
index c0a1f0c..9adef82 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HeadsetProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HeadsetProfileTest.java
@@ -11,7 +11,6 @@
import android.bluetooth.BluetoothProfile;
import android.content.Context;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
import org.junit.Before;
@@ -19,11 +18,12 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowBluetoothAdapter.class})
public class HeadsetProfileTest {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java
index cb1b12d..2b5466c 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java
@@ -29,16 +29,15 @@
import android.bluetooth.BluetoothProfile;
import android.content.Context;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class HearingAidDeviceManagerTest {
private final static long HISYNCID1 = 10;
private final static long HISYNCID2 = 11;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HfpClientProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HfpClientProfileTest.java
index 187be0b..69c020d 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HfpClientProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HfpClientProfileTest.java
@@ -18,18 +18,14 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.when;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadsetClient;
import android.bluetooth.BluetoothProfile;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
import org.junit.Before;
@@ -37,11 +33,12 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.robolectric.annotation.Config;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowBluetoothAdapter.class})
public class HfpClientProfileTest {
@@ -52,8 +49,6 @@
@Mock
private BluetoothHeadsetClient mService;
@Mock
- private CachedBluetoothDevice mCachedBluetoothDevice;
- @Mock
private BluetoothDevice mBluetoothDevice;
private BluetoothProfile.ServiceListener mServiceListener;
private HfpClientProfile mProfile;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HidDeviceProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HidDeviceProfileTest.java
index c91ee22..f38af70 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HidDeviceProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HidDeviceProfileTest.java
@@ -18,18 +18,14 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.when;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHidDevice;
import android.bluetooth.BluetoothProfile;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
import org.junit.Before;
@@ -37,11 +33,12 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.robolectric.annotation.Config;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowBluetoothAdapter.class})
public class HidDeviceProfileTest {
@@ -52,8 +49,6 @@
@Mock
private BluetoothHidDevice mService;
@Mock
- private CachedBluetoothDevice mCachedBluetoothDevice;
- @Mock
private BluetoothDevice mBluetoothDevice;
private BluetoothProfile.ServiceListener mServiceListener;
private HidDeviceProfile mProfile;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java
index a3c3a54..5d5872e 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java
@@ -37,7 +37,6 @@
import android.content.Intent;
import android.os.ParcelUuid;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
import org.junit.Before;
@@ -45,6 +44,7 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
@@ -52,7 +52,7 @@
import java.util.ArrayList;
import java.util.List;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowBluetoothAdapter.class})
public class LocalBluetoothProfileManagerTest {
private final static long HISYNCID = 10;
@@ -270,13 +270,13 @@
verify(mCachedBluetoothDevice).refresh();
}
- private List<Integer> generateList(int[] profile) {
- if (profile == null) {
+ private List<Integer> generateList(int[] profiles) {
+ if (profiles == null) {
return null;
}
- final List<Integer> profileList = new ArrayList<>(profile.length);
- for(int i = 0; i < profile.length; i++) {
- profileList.add(profile[i]);
+ final List<Integer> profileList = new ArrayList<>(profiles.length);
+ for (int profile : profiles) {
+ profileList.add(profile);
}
return profileList;
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java
index c4c48a8..6f66709 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java
@@ -18,18 +18,14 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.when;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothMapClient;
import android.bluetooth.BluetoothProfile;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
import org.junit.Before;
@@ -37,11 +33,12 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.robolectric.annotation.Config;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowBluetoothAdapter.class})
public class MapClientProfileTest {
@@ -52,8 +49,6 @@
@Mock
private BluetoothMapClient mService;
@Mock
- private CachedBluetoothDevice mCachedBluetoothDevice;
- @Mock
private BluetoothDevice mBluetoothDevice;
private BluetoothProfile.ServiceListener mServiceListener;
private MapClientProfile mProfile;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PbapClientProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PbapClientProfileTest.java
index e4a444c..b21ec9c3 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PbapClientProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PbapClientProfileTest.java
@@ -18,18 +18,14 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.when;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothPbapClient;
import android.bluetooth.BluetoothProfile;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
import org.junit.Before;
@@ -37,12 +33,13 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.robolectric.annotation.Config;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
-@RunWith(SettingsLibRobolectricTestRunner.class)
-@Config(shadows = {ShadowBluetoothAdapter.class})
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = ShadowBluetoothAdapter.class)
public class PbapClientProfileTest {
@Mock
@@ -52,8 +49,6 @@
@Mock
private BluetoothPbapClient mService;
@Mock
- private CachedBluetoothDevice mCachedBluetoothDevice;
- @Mock
private BluetoothDevice mBluetoothDevice;
private BluetoothProfile.ServiceListener mServiceListener;
private PbapClientProfile mProfile;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java
index 9bb53ee..ec88034 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java
@@ -18,18 +18,14 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.when;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothSap;
import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothSap;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
import org.junit.Before;
@@ -37,11 +33,12 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.robolectric.annotation.Config;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowBluetoothAdapter.class})
public class SapProfileTest {
@@ -52,8 +49,6 @@
@Mock
private BluetoothSap mService;
@Mock
- private CachedBluetoothDevice mCachedBluetoothDevice;
- @Mock
private BluetoothDevice mBluetoothDevice;
private BluetoothProfile.ServiceListener mServiceListener;
private SapProfile mProfile;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java
index 4d7553c..28de191 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java
@@ -24,16 +24,15 @@
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class AbstractPreferenceControllerTest {
@Mock
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java
index 4ec6fb2..8a0ae91 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java
@@ -27,7 +27,6 @@
import android.content.Intent;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
@@ -35,13 +34,14 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.util.ReflectionHelpers;
import java.util.ArrayList;
import java.util.List;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class MetricsFeatureProviderTest {
@Mock
private LogWriter mLogWriter;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SharedPreferenceLoggerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SharedPreferenceLoggerTest.java
index 6285fcd..8f51dec 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SharedPreferenceLoggerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SharedPreferenceLoggerTest.java
@@ -17,8 +17,8 @@
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_SETTINGS_PREFERENCE_CHANGE;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -26,16 +26,15 @@
import android.content.Context;
import android.content.SharedPreferences;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class SharedPreferenceLoggerTest {
private static final String TEST_TAG = "tag";
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java
index b251c09..097db17 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java
@@ -17,10 +17,10 @@
import static com.android.settingslib.core.instrumentation.Instrumentable.METRICS_CATEGORY_UNKNOWN;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
@@ -35,7 +35,6 @@
import androidx.fragment.app.FragmentActivity;
import com.android.internal.logging.nano.MetricsProto;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
@@ -43,10 +42,10 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.android.controller.ActivityController;
-
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class VisibilityLoggerMixinTest {
@Mock
@@ -139,7 +138,7 @@
private final class TestInstrumentable implements Instrumentable {
- public static final int TEST_METRIC = 12345;
+ private static final int TEST_METRIC = 12345;
@Override
public int getMetricsCategory() {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java
index 887c1d5..29e37e4 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java
@@ -28,7 +28,6 @@
import androidx.lifecycle.LifecycleOwner;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.core.lifecycle.events.OnAttach;
import com.android.settingslib.core.lifecycle.events.OnCreateOptionsMenu;
import com.android.settingslib.core.lifecycle.events.OnDestroy;
@@ -43,10 +42,11 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.android.controller.ActivityController;
import org.robolectric.shadows.androidx.fragment.FragmentController;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class LifecycleTest {
private LifecycleOwner mLifecycleOwner;
@@ -56,7 +56,7 @@
final TestObserver mFragObserver;
- public TestDialogFragment() {
+ private TestDialogFragment() {
mFragObserver = new TestObserver();
mLifecycle.addObserver(mFragObserver);
}
@@ -236,11 +236,11 @@
}
private static class OptionItemAccepter implements LifecycleObserver, OnOptionsItemSelected {
- public boolean wasCalled = false;
+ private boolean mWasCalled = false;
@Override
public boolean onOptionsItemSelected(MenuItem menuItem) {
- wasCalled = true;
+ mWasCalled = true;
return false;
}
}
@@ -258,14 +258,14 @@
fragment.onPrepareOptionsMenu(null);
fragment.onOptionsItemSelected(null);
- assertThat(accepter.wasCalled).isFalse();
+ assertThat(accepter.mWasCalled).isFalse();
}
private class OnStartObserver implements LifecycleObserver, OnStart {
private final Lifecycle mLifecycle;
- public OnStartObserver(Lifecycle lifecycle) {
+ private OnStartObserver(Lifecycle lifecycle) {
mLifecycle = lifecycle;
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DeveloperOptionsPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DeveloperOptionsPreferenceControllerTest.java
index 9dd93b3a..6191a00 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DeveloperOptionsPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DeveloperOptionsPreferenceControllerTest.java
@@ -22,16 +22,15 @@
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class DeveloperOptionsPreferenceControllerTest {
private static final String TEST_KEY = "Test_pref_key";
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DevelopmentSettingsEnablerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DevelopmentSettingsEnablerTest.java
index a0fa6b5..3475ff7 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DevelopmentSettingsEnablerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DevelopmentSettingsEnablerTest.java
@@ -18,23 +18,19 @@
import static com.google.common.truth.Truth.assertThat;
-import android.os.UserManager;
import android.content.Context;
+import android.os.UserManager;
import android.provider.Settings;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
-import org.robolectric.shadows.ShadowUserManager;
-import org.robolectric.shadow.api.Shadow;
-
-import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.Config;
+import org.robolectric.shadow.api.Shadow;
+import org.robolectric.shadows.ShadowUserManager;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class DevelopmentSettingsEnablerTest {
private Context mContext;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java
index d7b23b0..e84a25c 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java
@@ -32,17 +32,16 @@
import androidx.preference.PreferenceScreen;
import androidx.preference.SwitchPreference;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.shadows.ShadowApplication;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class EnableAdbPreferenceControllerTest {
@Mock(answer = RETURNS_DEEP_STUBS)
private PreferenceScreen mScreen;
@@ -150,7 +149,7 @@
}
class ConcreteEnableAdbPreferenceController extends AbstractEnableAdbPreferenceController {
- public ConcreteEnableAdbPreferenceController(Context context) {
+ private ConcreteEnableAdbPreferenceController(Context context) {
super(context);
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogdSizePreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogdSizePreferenceControllerTest.java
index 2f78899..146be23 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogdSizePreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogdSizePreferenceControllerTest.java
@@ -45,16 +45,16 @@
import androidx.preference.PreferenceScreen;
import com.android.settingslib.R;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class LogdSizePreferenceControllerTest {
@Mock
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogpersistPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogpersistPreferenceControllerTest.java
index ed128e0..d5afb4b 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogpersistPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogpersistPreferenceControllerTest.java
@@ -29,7 +29,6 @@
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.Before;
@@ -37,9 +36,10 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class LogpersistPreferenceControllerTest {
private LifecycleOwner mLifecycleOwner;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/SystemPropPokerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/SystemPropPokerTest.java
index 40db478..d1212fc 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/SystemPropPokerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/SystemPropPokerTest.java
@@ -27,16 +27,15 @@
import android.os.IBinder;
import android.os.Parcel;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
+import org.robolectric.RobolectricTestRunner;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class SystemPropPokerTest {
@Spy
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/BluetoothAddressPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/BluetoothAddressPreferenceControllerTest.java
index 234b4d5..16de5f8 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/BluetoothAddressPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/BluetoothAddressPreferenceControllerTest.java
@@ -26,7 +26,6 @@
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.Before;
@@ -34,11 +33,12 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class BluetoothAddressPreferenceControllerTest {
@Mock
private Context mContext;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ConnectivityPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ConnectivityPreferenceControllerTest.java
index aee956c..4444e63 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ConnectivityPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ConnectivityPreferenceControllerTest.java
@@ -30,7 +30,6 @@
import android.content.IntentFilter;
import android.os.Handler;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.Before;
@@ -39,8 +38,9 @@
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class ConnectivityPreferenceControllerTest {
@Mock
private Context mContext;
@@ -91,8 +91,7 @@
private static class ConcreteConnectivityPreferenceController
extends AbstractConnectivityPreferenceController {
-
- public ConcreteConnectivityPreferenceController(Context context,
+ private ConcreteConnectivityPreferenceController(Context context,
Lifecycle lifecycle) {
super(context, lifecycle);
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ImsStatusPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ImsStatusPreferenceControllerTest.java
index 2b490ee..bd223bd 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ImsStatusPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ImsStatusPreferenceControllerTest.java
@@ -25,12 +25,10 @@
import android.content.Context;
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
-import android.telephony.SubscriptionManager;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.Before;
@@ -38,11 +36,10 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.robolectric.annotation.Config;
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.shadows.ShadowSubscriptionManager;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class ImsStatusPreferenceControllerTest {
@Mock
private Context mContext;
@@ -61,8 +58,9 @@
}
@Test
- @Config(shadows = ShadowSubscriptionManager.class)
public void testIsAvailable() {
+ ShadowSubscriptionManager.setDefaultDataSubscriptionId(1234);
+
CarrierConfigManager carrierConfigManager = mock(CarrierConfigManager.class);
doReturn(carrierConfigManager).when(mContext).getSystemService(CarrierConfigManager.class);
@@ -92,18 +90,10 @@
.that(imsStatusPreferenceController.isAvailable()).isFalse();
}
- @Implements(SubscriptionManager.class)
- public static class ShadowSubscriptionManager {
- @Implementation
- public static int getDefaultDataSubscriptionId() {
- return 1234;
- }
- }
-
private static class ConcreteImsStatusPreferenceController
extends AbstractImsStatusPreferenceController {
- public ConcreteImsStatusPreferenceController(Context context,
+ private ConcreteImsStatusPreferenceController(Context context,
Lifecycle lifecycle) {
super(context, lifecycle);
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/IpAddressPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/IpAddressPreferenceControllerTest.java
index 1d957c3..76a26d9 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/IpAddressPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/IpAddressPreferenceControllerTest.java
@@ -27,7 +27,6 @@
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.Before;
@@ -35,11 +34,12 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import java.util.Arrays;
import java.util.List;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class IpAddressPreferenceControllerTest {
@Mock
private Context mContext;
@@ -75,8 +75,7 @@
private static class ConcreteIpAddressPreferenceController extends
AbstractIpAddressPreferenceController {
- public ConcreteIpAddressPreferenceController(Context context,
- Lifecycle lifecycle) {
+ private ConcreteIpAddressPreferenceController(Context context, Lifecycle lifecycle) {
super(context, lifecycle);
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SerialNumberPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SerialNumberPreferenceControllerTest.java
index dc77400..5b71bdd 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SerialNumberPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SerialNumberPreferenceControllerTest.java
@@ -25,16 +25,15 @@
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class SerialNumberPreferenceControllerTest {
@Mock
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SimStatusImeiInfoPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SimStatusImeiInfoPreferenceControllerTest.java
index eb77cb6..5252c6c 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SimStatusImeiInfoPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SimStatusImeiInfoPreferenceControllerTest.java
@@ -24,17 +24,16 @@
import android.os.UserManager;
import android.util.SparseBooleanArray;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
@Config(shadows = {SimStatusImeiInfoPreferenceControllerTest.ShadowUserManager.class,
SimStatusImeiInfoPreferenceControllerTest.ShadowConnectivityManager.class})
public class SimStatusImeiInfoPreferenceControllerTest {
@@ -106,7 +105,7 @@
private final SparseBooleanArray mSupportedNetworkTypes = new SparseBooleanArray();
- public void setNetworkSupported(int networkType, boolean supported) {
+ private void setNetworkSupported(int networkType, boolean supported) {
mSupportedNetworkTypes.put(networkType, supported);
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/UptimePreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/UptimePreferenceControllerTest.java
index 2e0348d..f09879b 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/UptimePreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/UptimePreferenceControllerTest.java
@@ -28,7 +28,6 @@
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.Before;
@@ -36,9 +35,10 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.shadows.ShadowLooper;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class UptimePreferenceControllerTest {
@Mock
private Context mContext;
@@ -92,7 +92,7 @@
private static class ConcreteUptimePreferenceController
extends AbstractUptimePreferenceController {
- public ConcreteUptimePreferenceController(Context context,
+ private ConcreteUptimePreferenceController(Context context,
Lifecycle lifecycle) {
super(context, lifecycle);
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java
index 359ea77..74e5bf5 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java
@@ -33,7 +33,6 @@
import androidx.preference.PreferenceScreen;
import com.android.settingslib.R;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.Before;
@@ -41,13 +40,14 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.Arrays;
import java.util.List;
@SuppressLint("HardwareIds")
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class WifiMacAddressPreferenceControllerTest {
@Mock
private Lifecycle mLifecycle;
@@ -197,7 +197,7 @@
private static class ConcreteWifiMacAddressPreferenceController
extends AbstractWifiMacAddressPreferenceController {
- public ConcreteWifiMacAddressPreferenceController(Context context,
+ private ConcreteWifiMacAddressPreferenceController(Context context,
Lifecycle lifecycle) {
super(context, lifecycle);
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/display/BrightnessUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/display/BrightnessUtilsTest.java
index ca621ca..c0924d9 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/display/BrightnessUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/display/BrightnessUtilsTest.java
@@ -20,12 +20,11 @@
import static com.google.common.truth.Truth.assertThat;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class BrightnessUtilsTest {
private static final int MIN = 1;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryKeyTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryKeyTest.java
index 59a3dd6..605c861 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryKeyTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryKeyTest.java
@@ -20,14 +20,13 @@
import android.util.ArraySet;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import java.util.Set;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class CategoryKeyTest {
@Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java
index 40e7386..b77670b 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java
@@ -13,15 +13,14 @@
import android.os.Bundle;
import com.android.settingslib.R;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class TileTest {
private ActivityInfo mActivityInfo;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
index 362ae4c..bbb4249 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
@@ -54,7 +54,6 @@
import android.util.Pair;
import com.android.settingslib.R;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
@@ -62,12 +61,13 @@
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class TileUtilsTest {
private Context mContext;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/BatterySaverUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/BatterySaverUtilsTest.java
index d0b6dab..2988905 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/BatterySaverUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/BatterySaverUtilsTest.java
@@ -33,28 +33,26 @@
import android.provider.Settings.Global;
import android.provider.Settings.Secure;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
-
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class BatterySaverUtilsTest {
- final int BATTERY_SAVER_THRESHOLD_1 = 15;
- final int BATTERY_SAVER_THRESHOLD_2 = 20;
+ private static final int BATTERY_SAVER_THRESHOLD_1 = 15;
+ private static final int BATTERY_SAVER_THRESHOLD_2 = 20;
@Mock
- Context mMockContext;
+ private Context mMockContext;
@Mock
- ContentResolver mMockResolver;
+ private ContentResolver mMockResolver;
@Mock
- PowerManager mMockPowerManager;
+ private PowerManager mMockPowerManager;
@Before
public void setUp() throws Exception {
@@ -66,11 +64,11 @@
}
@Test
- public void testSetPowerSaveMode_enable_firstCall_needWarning() throws Exception {
+ public void testSetPowerSaveMode_enable_firstCall_needWarning() {
Secure.putString(mMockResolver, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, "null");
Secure.putString(mMockResolver, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, "null");
- assertEquals(false, BatterySaverUtils.setPowerSaveMode(mMockContext, true, true));
+ assertThat(BatterySaverUtils.setPowerSaveMode(mMockContext, true, true)).isFalse();
verify(mMockContext, times(1)).sendBroadcast(any(Intent.class));
verify(mMockPowerManager, times(0)).setPowerSaveMode(anyBoolean());
@@ -83,11 +81,11 @@
}
@Test
- public void testSetPowerSaveMode_enable_secondCall_needWarning() throws Exception {
+ public void testSetPowerSaveMode_enable_secondCall_needWarning() {
Secure.putInt(mMockResolver, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, 1); // Already acked.
Secure.putString(mMockResolver, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, "null");
- assertEquals(true, BatterySaverUtils.setPowerSaveMode(mMockContext, true, true));
+ assertThat(BatterySaverUtils.setPowerSaveMode(mMockContext, true, true)).isTrue();
verify(mMockContext, times(0)).sendBroadcast(any(Intent.class));
verify(mMockPowerManager, times(1)).setPowerSaveMode(eq(true));
@@ -97,11 +95,11 @@
}
@Test
- public void testSetPowerSaveMode_enable_thridCall_needWarning() throws Exception {
+ public void testSetPowerSaveMode_enable_thridCall_needWarning() {
Secure.putInt(mMockResolver, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, 1); // Already acked.
Secure.putInt(mMockResolver, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, 1);
- assertEquals(true, BatterySaverUtils.setPowerSaveMode(mMockContext, true, true));
+ assertThat(BatterySaverUtils.setPowerSaveMode(mMockContext, true, true)).isTrue();
verify(mMockContext, times(0)).sendBroadcast(any(Intent.class));
verify(mMockPowerManager, times(1)).setPowerSaveMode(eq(true));
@@ -111,11 +109,11 @@
}
@Test
- public void testSetPowerSaveMode_enable_firstCall_noWarning() throws Exception {
+ public void testSetPowerSaveMode_enable_firstCall_noWarning() {
Secure.putString(mMockResolver, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, "null");
Secure.putString(mMockResolver, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, "null");
- assertEquals(true, BatterySaverUtils.setPowerSaveMode(mMockContext, true, false));
+ assertThat(BatterySaverUtils.setPowerSaveMode(mMockContext, true, false)).isTrue();
verify(mMockContext, times(0)).sendBroadcast(any(Intent.class));
verify(mMockPowerManager, times(1)).setPowerSaveMode(eq(true));
@@ -125,12 +123,12 @@
}
@Test
- public void testSetPowerSaveMode_disable_firstCall_noWarning() throws Exception {
+ public void testSetPowerSaveMode_disable_firstCall_noWarning() {
Secure.putString(mMockResolver, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, "null");
Secure.putString(mMockResolver, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, "null");
// When disabling, needFirstTimeWarning doesn't matter.
- assertEquals(true, BatterySaverUtils.setPowerSaveMode(mMockContext, false, false));
+ assertThat(BatterySaverUtils.setPowerSaveMode(mMockContext, false, false)).isTrue();
verify(mMockContext, times(0)).sendBroadcast(any(Intent.class));
verify(mMockPowerManager, times(1)).setPowerSaveMode(eq(false));
@@ -141,12 +139,12 @@
}
@Test
- public void testSetPowerSaveMode_disable_firstCall_needWarning() throws Exception {
+ public void testSetPowerSaveMode_disable_firstCall_needWarning() {
Secure.putString(mMockResolver, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, "null");
Secure.putString(mMockResolver, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, "null");
// When disabling, needFirstTimeWarning doesn't matter.
- assertEquals(true, BatterySaverUtils.setPowerSaveMode(mMockContext, false, true));
+ assertThat(BatterySaverUtils.setPowerSaveMode(mMockContext, false, true)).isTrue();
verify(mMockContext, times(0)).sendBroadcast(any(Intent.class));
verify(mMockPowerManager, times(1)).setPowerSaveMode(eq(false));
@@ -157,7 +155,7 @@
}
@Test
- public void testEnsureAutoBatterysaver_setNewPositiveValue_doNotOverwrite() throws Exception {
+ public void testEnsureAutoBatterysaver_setNewPositiveValue_doNotOverwrite() {
Global.putInt(mMockResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0);
BatterySaverUtils.ensureAutoBatterySaver(mMockContext, BATTERY_SAVER_THRESHOLD_1);
@@ -172,7 +170,7 @@
}
@Test
- public void testSetAutoBatterySaverTriggerLevel_setSuppressSuggestion() throws Exception {
+ public void testSetAutoBatterySaverTriggerLevel_setSuppressSuggestion() {
Global.putString(mMockResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, "null");
Secure.putString(mMockResolver, Secure.SUPPRESS_AUTO_BATTERY_SAVER_SUGGESTION, "null");
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerWhitelistBackendTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerWhitelistBackendTest.java
index 9b1fe5f..bbf807d2 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerWhitelistBackendTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerWhitelistBackendTest.java
@@ -31,7 +31,6 @@
import android.content.pm.PackageManager;
import android.os.IDeviceIdleController;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.testutils.shadow.ShadowDefaultDialerManager;
import com.android.settingslib.testutils.shadow.ShadowSmsApplication;
@@ -40,12 +39,13 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.ShadowPackageManager;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowDefaultDialerManager.class, ShadowSmsApplication.class})
public class PowerWhitelistBackendTest {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BatteryMeterDrawableBaseTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BatteryMeterDrawableBaseTest.java
index 49dde0e..35743c2 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BatteryMeterDrawableBaseTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BatteryMeterDrawableBaseTest.java
@@ -16,8 +16,8 @@
package com.android.settingslib.graph;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@@ -25,17 +25,16 @@
import android.graphics.Canvas;
import android.graphics.Paint;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.util.ReflectionHelpers;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class BatteryMeterDrawableBaseTest {
private static final int CRITICAL_LEVEL = 5;
private static final int PADDING = 5;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawableTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawableTest.java
index 5dbb5ca..1b350cc 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawableTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawableTest.java
@@ -22,14 +22,14 @@
import android.graphics.drawable.VectorDrawable;
import com.android.settingslib.R;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class BluetoothDeviceLayerDrawableTest {
private static final int RES_ID = R.drawable.ic_bt_cellphone;
private static final int BATTERY_LEVEL = 15;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompatTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompatTest.java
index fa64afe..b930aa6 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompatTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompatTest.java
@@ -25,26 +25,21 @@
import android.view.inputmethod.InputMethodSubtype;
import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class InputMethodAndSubtypeUtilCompatTest {
private static final HashSet<String> EMPTY_STRING_SET = new HashSet<>();
private static HashSet<String> asHashSet(String... strings) {
- HashSet<String> hashSet = new HashSet<>();
- for (String s : strings) {
- hashSet.add(s);
- }
- return hashSet;
+ return new HashSet<>(Arrays.asList(strings));
}
@Test
@@ -105,7 +100,6 @@
"ime0;subtype0;subtype1:ime1;subtype1;subtype2"))
.containsExactly("ime0", asHashSet("subtype0", "subtype1"),
"ime1", asHashSet("subtype1", "subtype2"));
-
}
@Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java
index 03ab261..84606b4 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java
@@ -25,26 +25,21 @@
import android.view.inputmethod.InputMethodSubtype;
import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class InputMethodAndSubtypeUtilTest {
private static final HashSet<String> EMPTY_STRING_SET = new HashSet<>();
private static HashSet<String> asHashSet(String... strings) {
- HashSet<String> hashSet = new HashSet<>();
- for (String s : strings) {
- hashSet.add(s);
- }
- return hashSet;
+ return new HashSet<>(Arrays.asList(strings));
}
@Test
@@ -103,7 +98,6 @@
"ime0;subtype0;subtype1:ime1;subtype1;subtype2"))
.containsExactly("ime0", asHashSet("subtype0", "subtype1"),
"ime1", asHashSet("subtype1", "subtype2"));
-
}
@Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java
index b00476b2..4b5e909 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java
@@ -18,10 +18,9 @@
import static com.google.common.truth.Truth.assertThat;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import org.xmlpull.v1.XmlPullParserException;
import java.io.ByteArrayInputStream;
@@ -32,7 +31,7 @@
import java.util.HashMap;
import java.util.Map;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class LicenseHtmlGeneratorFromXmlTest {
private static final String VALILD_XML_STRING =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
@@ -92,8 +91,8 @@
@Test
public void testParseValidXmlStream() throws XmlPullParserException, IOException {
- Map<String, String> fileNameToContentIdMap = new HashMap<String, String>();
- Map<String, String> contentIdToFileContentMap = new HashMap<String, String>();
+ Map<String, String> fileNameToContentIdMap = new HashMap<>();
+ Map<String, String> contentIdToFileContentMap = new HashMap<>();
LicenseHtmlGeneratorFromXml.parse(
new InputStreamReader(new ByteArrayInputStream(VALILD_XML_STRING.getBytes())),
@@ -107,8 +106,8 @@
@Test(expected = XmlPullParserException.class)
public void testParseInvalidXmlStream() throws XmlPullParserException, IOException {
- Map<String, String> fileNameToContentIdMap = new HashMap<String, String>();
- Map<String, String> contentIdToFileContentMap = new HashMap<String, String>();
+ Map<String, String> fileNameToContentIdMap = new HashMap<>();
+ Map<String, String> contentIdToFileContentMap = new HashMap<>();
LicenseHtmlGeneratorFromXml.parse(
new InputStreamReader(new ByteArrayInputStream(INVALILD_XML_STRING.getBytes())),
@@ -117,8 +116,8 @@
@Test
public void testGenerateHtml() {
- Map<String, String> fileNameToContentIdMap = new HashMap<String, String>();
- Map<String, String> contentIdToFileContentMap = new HashMap<String, String>();
+ Map<String, String> fileNameToContentIdMap = new HashMap<>();
+ Map<String, String> contentIdToFileContentMap = new HashMap<>();
fileNameToContentIdMap.put("/file0", "0");
fileNameToContentIdMap.put("/file1", "0");
@@ -132,8 +131,8 @@
@Test
public void testGenerateHtmlWithCustomHeading() {
- Map<String, String> fileNameToContentIdMap = new HashMap<String, String>();
- Map<String, String> contentIdToFileContentMap = new HashMap<String, String>();
+ Map<String, String> fileNameToContentIdMap = new HashMap<>();
+ Map<String, String> contentIdToFileContentMap = new HashMap<>();
fileNameToContentIdMap.put("/file0", "0");
fileNameToContentIdMap.put("/file1", "0");
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlLoaderCompatTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlLoaderCompatTest.java
index c90de5f..e82bc06 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlLoaderCompatTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlLoaderCompatTest.java
@@ -20,14 +20,13 @@
import android.content.Context;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
@@ -37,7 +36,7 @@
import java.util.ArrayList;
import java.util.List;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
@Config(shadows = LicenseHtmlLoaderCompatTest.ShadowLicenseHtmlLoaderCompat.class)
public class LicenseHtmlLoaderCompatTest {
@@ -58,7 +57,7 @@
@Test
public void testLoadInBackground() {
- ArrayList<File> xmlFiles = new ArrayList();
+ ArrayList<File> xmlFiles = new ArrayList<>();
xmlFiles.add(new File("test.xml"));
File cachedHtmlFile = new File("test.html");
@@ -69,7 +68,7 @@
@Test
public void testLoadInBackgroundWithNoVaildXmlFiles() {
- ArrayList<File> xmlFiles = new ArrayList();
+ ArrayList<File> xmlFiles = new ArrayList<>();
File cachedHtmlFile = new File("test.html");
setupFakeData(xmlFiles, cachedHtmlFile, true, true);
@@ -79,7 +78,7 @@
@Test
public void testLoadInBackgroundWithNonOutdatedCachedHtmlFile() {
- ArrayList<File> xmlFiles = new ArrayList();
+ ArrayList<File> xmlFiles = new ArrayList<>();
xmlFiles.add(new File("test.xml"));
File cachedHtmlFile = new File("test.html");
@@ -90,7 +89,7 @@
@Test
public void testLoadInBackgroundWithGenerateHtmlFileFailed() {
- ArrayList<File> xmlFiles = new ArrayList();
+ ArrayList<File> xmlFiles = new ArrayList<>();
xmlFiles.add(new File("test.xml"));
File cachedHtmlFile = new File("test.html");
@@ -112,10 +111,10 @@
@Implements(LicenseHtmlLoaderCompat.class)
public static class ShadowLicenseHtmlLoaderCompat {
- public static List<File> sValidXmlFiles;
- public static File sCachedHtmlFile;
- public static boolean sIsCachedHtmlFileOutdated;
- public static boolean sGenerateHtmlFileSucceeded;
+ private static List<File> sValidXmlFiles;
+ private static File sCachedHtmlFile;
+ private static boolean sIsCachedHtmlFileOutdated;
+ private static boolean sGenerateHtmlFileSucceeded;
@Resetter
public static void reset() {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/InjectedSettingTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/InjectedSettingTest.java
index c29481f..8c2e899 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/InjectedSettingTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/InjectedSettingTest.java
@@ -18,12 +18,11 @@
import static com.google.common.truth.Truth.assertThat;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public final class InjectedSettingTest {
private static final String TEST_STRING = "test";
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java
index 9c168f7..08d5367 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java
@@ -2,7 +2,7 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.isA;
+import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.Mockito.when;
import android.app.AppOpsManager;
@@ -17,20 +17,19 @@
import android.os.UserHandle;
import android.os.UserManager;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class RecentLocationAppsTest {
private static final int TEST_UID = 1234;
@@ -56,8 +55,6 @@
private int mTestUserId;
private RecentLocationApps mRecentLocationApps;
-
-
@Before
public void setUp() throws NameNotFoundException {
MockitoAnnotations.initMocks(this);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java
index 50044f2..72ed5e1 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java
@@ -42,16 +42,15 @@
import android.text.format.DateUtils;
import android.util.FeatureFlagUtils;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class DataUsageControllerTest {
private static final String SUB_ID = "Test Subscriber";
@@ -85,7 +84,6 @@
doReturn(null).when(mController).getSession();
assertThat(mController.getHistoricalUsageLevel(null /* template */)).isEqualTo(-1L);
-
}
@Test
@@ -95,7 +93,6 @@
assertThat(mController.getHistoricalUsageLevel(NetworkTemplate.buildTemplateWifiWildcard()))
.isEqualTo(0L);
-
}
@Test
@@ -113,7 +110,6 @@
assertThat(mController.getHistoricalUsageLevel(NetworkTemplate.buildTemplateWifiWildcard()))
.isEqualTo(receivedBytes + transmittedBytes);
-
}
@Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleChartDataLoaderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleChartDataLoaderTest.java
index 0a03631..011f234 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleChartDataLoaderTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleChartDataLoaderTest.java
@@ -27,15 +27,14 @@
import android.os.RemoteException;
import android.text.format.DateUtils;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class NetworkCycleChartDataLoaderTest {
@Mock
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java
index 2314f27..d915963 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java
@@ -19,7 +19,7 @@
import static android.app.usage.NetworkStats.Bucket.STATE_FOREGROUND;
import static android.net.NetworkStats.TAG_NONE;
-import static org.mockito.Matchers.any;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
@@ -33,15 +33,14 @@
import android.net.NetworkPolicyManager;
import android.text.format.DateUtils;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class NetworkCycleDataForUidLoaderTest {
@Mock
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java
index 9d60a97..2d8ea12 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java
@@ -16,8 +16,8 @@
package com.android.settingslib.net;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.nullable;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
@@ -37,20 +37,19 @@
import android.text.format.DateUtils;
import android.util.Range;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.util.ReflectionHelpers;
import java.time.ZonedDateTime;
import java.util.Iterator;
import java.util.List;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class NetworkCycleDataLoaderTest {
@Mock
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/EnableZenModeDialogTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/EnableZenModeDialogTest.java
index 89c319a..59d5674 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/EnableZenModeDialogTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/EnableZenModeDialogTest.java
@@ -16,11 +16,10 @@
package com.android.settingslib.notification;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertNotNull;
-import static junit.framework.Assert.assertNull;
-import static junit.framework.Assert.assertTrue;
-
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
@@ -38,16 +37,15 @@
import android.service.notification.Condition;
import android.view.LayoutInflater;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class EnableZenModeDialogTest {
private EnableZenModeDialog mController;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/ZenDurationDialogTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/ZenDurationDialogTest.java
index 8147656..437c0d4 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/ZenDurationDialogTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/ZenDurationDialogTest.java
@@ -25,27 +25,23 @@
import android.content.ContentResolver;
import android.content.Context;
import android.provider.Settings;
-import android.service.notification.Condition;
import android.view.LayoutInflater;
import android.view.View;
import androidx.appcompat.app.AlertDialog;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class ZenDurationDialogTest {
private ZenDurationDialog mController;
private Context mContext;
private LayoutInflater mLayoutInflater;
- private Condition mCountdownCondition;
- private Condition mAlarmCondition;
private ContentResolver mContentResolver;
private AlertDialog.Builder mBuilder;
@@ -102,7 +98,6 @@
ZenDurationDialog.ALWAYS_ASK_CONDITION_INDEX).rb.isChecked());
}
-
@Test
public void testChooseAlwaysPromptSetting() {
Settings.Secure.putInt(mContentResolver, Settings.Secure.ZEN_DURATION,
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionControllerMixinCompatTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionControllerMixinCompatTest.java
index 449451a..ffaa7443 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionControllerMixinCompatTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionControllerMixinCompatTest.java
@@ -31,7 +31,6 @@
import androidx.lifecycle.LifecycleOwner;
import androidx.loader.app.LoaderManager;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.After;
@@ -40,10 +39,11 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
@Config(shadows = ShadowSuggestionController.class)
public class SuggestionControllerMixinCompatTest {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionControllerMixinTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionControllerMixinTest.java
index aac582f..4dc80f4 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionControllerMixinTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionControllerMixinTest.java
@@ -31,7 +31,6 @@
import androidx.lifecycle.LifecycleOwner;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.After;
@@ -40,10 +39,11 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
@Config(shadows = ShadowSuggestionController.class)
public class SuggestionControllerMixinTest {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowDefaultDialerManager.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowDefaultDialerManager.java
index f4afdb1..3e91641 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowDefaultDialerManager.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowDefaultDialerManager.java
@@ -26,19 +26,19 @@
@Implements(DefaultDialerManager.class)
public class ShadowDefaultDialerManager {
- private static String sDefaultDailer;
+ private static String sDefaultDialer;
@Resetter
public void reset() {
- sDefaultDailer = null;
+ sDefaultDialer = null;
}
@Implementation
public static String getDefaultDialerApplication(Context context) {
- return sDefaultDailer;
+ return sDefaultDialer;
}
public static void setDefaultDialerApplication(String dialer) {
- sDefaultDailer = dialer;
+ sDefaultDialer = dialer;
}
}
\ No newline at end of file
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/UserManagerHelperRoboTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/UserManagerHelperRoboTest.java
index 4705cd2..9a169d2 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/UserManagerHelperRoboTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/UserManagerHelperRoboTest.java
@@ -27,7 +27,6 @@
import android.os.UserHandle;
import android.os.UserManager;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.testutils.shadow.ShadowActivityManager;
import org.junit.After;
@@ -36,6 +35,7 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.annotation.Implementation;
@@ -45,7 +45,7 @@
import java.util.ArrayList;
import java.util.List;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
@Config(shadows = { ShadowActivityManager.class, UserManagerHelperRoboTest.ShadowUserHandle.class})
public class UserManagerHelperRoboTest {
@Mock
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/IconCacheTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/IconCacheTest.java
index 645dfa1..026ad47 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/IconCacheTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/IconCacheTest.java
@@ -30,14 +30,13 @@
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class IconCacheTest {
private Icon mIcon;
private Context mContext;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
index 6a9579b..7ef31df 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
@@ -22,31 +22,30 @@
import android.content.Context;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.time.Duration;
import java.util.regex.Pattern;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class PowerUtilTest {
- public static final String TEST_BATTERY_LEVEL_10 = "10%";
- public static final long SEVENTEEN_MIN_MILLIS = Duration.ofMinutes(17).toMillis();
- public static final long FIVE_MINUTES_MILLIS = Duration.ofMinutes(5).toMillis();
- public static final long TEN_MINUTES_MILLIS = Duration.ofMinutes(10).toMillis();
- public static final long THREE_DAYS_MILLIS = Duration.ofDays(3).toMillis();
- public static final long THIRTY_HOURS_MILLIS = Duration.ofHours(30).toMillis();
- public static final String NORMAL_CASE_EXPECTED_PREFIX = "Should last until about";
- public static final String ENHANCED_SUFFIX = " based on your usage";
+ private static final String TEST_BATTERY_LEVEL_10 = "10%";
+ private static final long SEVENTEEN_MIN_MILLIS = Duration.ofMinutes(17).toMillis();
+ private static final long FIVE_MINUTES_MILLIS = Duration.ofMinutes(5).toMillis();
+ private static final long TEN_MINUTES_MILLIS = Duration.ofMinutes(10).toMillis();
+ private static final long THREE_DAYS_MILLIS = Duration.ofDays(3).toMillis();
+ private static final long THIRTY_HOURS_MILLIS = Duration.ofHours(30).toMillis();
+ private static final String NORMAL_CASE_EXPECTED_PREFIX = "Should last until about";
+ private static final String ENHANCED_SUFFIX = " based on your usage";
// matches a time (ex: '1:15 PM', '2 AM', '23:00')
- public static final String TIME_OF_DAY_REGEX = " (\\d)+:?(\\d)* ((AM)*)|((PM)*)";
+ private static final String TIME_OF_DAY_REGEX = " (\\d)+:?(\\d)* ((AM)*)|((PM)*)";
// matches a percentage with parenthesis (ex: '(10%)')
- public static final String PERCENTAGE_REGEX = " \\(\\d?\\d%\\)";
+ private static final String PERCENTAGE_REGEX = " \\(\\d?\\d%\\)";
private Context mContext;
@@ -108,7 +107,6 @@
+ "(" + PERCENTAGE_REGEX + "){0}")); // no percentage
}
-
@Test
public void testGetBatteryRemainingStringFormatted_lessThanSevenMinutes_usesCorrectString() {
String info = PowerUtil.getBatteryRemainingStringFormatted(mContext,
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/StringUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/StringUtilTest.java
index e4bbbcb..8fbbfbb 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/StringUtilTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/StringUtilTest.java
@@ -25,14 +25,13 @@
import android.text.format.DateUtils;
import android.text.style.TtsSpan;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class StringUtilTest {
private Context mContext;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/ThreadUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/ThreadUtilsTest.java
index 1e066b1..26db124 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/ThreadUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/ThreadUtilsTest.java
@@ -15,28 +15,22 @@
*/
package com.android.settingslib.utils;
-
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.shadows.ShadowLooper;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class ThreadUtilsTest {
@Test
public void testMainThread() throws InterruptedException {
assertThat(ThreadUtils.isMainThread()).isTrue();
- Thread background = new Thread(new Runnable() {
- public void run() {
- assertThat(ThreadUtils.isMainThread()).isFalse();
- }
- });
+ Thread background = new Thread(() -> assertThat(ThreadUtils.isMainThread()).isFalse());
background.start();
background.join();
}
@@ -44,13 +38,11 @@
@Test
public void testEnsureMainThread() throws InterruptedException {
ThreadUtils.ensureMainThread();
- Thread background = new Thread(new Runnable() {
- public void run() {
- try {
- ThreadUtils.ensureMainThread();
- fail("Should not pass ensureMainThread in a background thread");
- } catch (RuntimeException e) {
- }
+ Thread background = new Thread(() -> {
+ try {
+ ThreadUtils.ensureMainThread();
+ fail("Should not pass ensureMainThread in a background thread");
+ } catch (RuntimeException expected) {
}
});
background.start();
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AnimatedImageViewTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AnimatedImageViewTest.java
index a00f12d..d41d511 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AnimatedImageViewTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AnimatedImageViewTest.java
@@ -22,14 +22,13 @@
import android.graphics.drawable.AnimatedRotateDrawable;
import android.view.View;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class AnimatedImageViewTest {
private AnimatedImageView mAnimatedImageView;
@@ -47,5 +46,4 @@
AnimatedRotateDrawable drawable = (AnimatedRotateDrawable) mAnimatedImageView.getDrawable();
assertThat(drawable.isRunning()).isTrue();
}
-
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinCompatTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinCompatTest.java
index e030005..601da051 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinCompatTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinCompatTest.java
@@ -18,7 +18,7 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.any;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -29,7 +29,6 @@
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.Before;
@@ -37,9 +36,10 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class FooterPreferenceMixinCompatTest {
@Mock
@@ -97,5 +97,4 @@
verify(mScreen).removePreference(any(FooterPreference.class));
verify(mScreen, times(2)).addPreference(any(FooterPreference.class));
}
-
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinTest.java
index 8817ff7..7ae5d2d 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinTest.java
@@ -18,7 +18,7 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.any;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -29,7 +29,6 @@
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.Before;
@@ -37,10 +36,10 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-import org.robolectric.shadows.ShadowApplication;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class FooterPreferenceMixinTest {
@Mock
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java
index e0eceb4..0d2399e 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java
@@ -26,14 +26,14 @@
import androidx.preference.PreferenceViewHolder;
import com.android.settingslib.R;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class FooterPreferenceTest {
private Context mContext;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/LayoutPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/LayoutPreferenceTest.java
index 427a611..99261a3 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/LayoutPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/LayoutPreferenceTest.java
@@ -27,14 +27,13 @@
import androidx.preference.Preference.OnPreferenceClickListener;
import androidx.preference.PreferenceViewHolder;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class LayoutPreferenceTest {
private LayoutPreference mPreference;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/apppreference/AppPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/apppreference/AppPreferenceTest.java
index 10c9dfb..da97cc8 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/apppreference/AppPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/apppreference/AppPreferenceTest.java
@@ -23,14 +23,13 @@
import androidx.preference.PreferenceViewHolder;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class AppPreferenceTest {
private Context mContext;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java
index 86443bd..c5cbea7 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java
@@ -25,16 +25,15 @@
import android.content.Context;
import android.graphics.drawable.ColorDrawable;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class AccessPointPreferenceTest {
private Context mContext = RuntimeEnvironment.application;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/TimestampedScoredNetworkTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/TimestampedScoredNetworkTest.java
index f0e8c66..b059df1 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/TimestampedScoredNetworkTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/TimestampedScoredNetworkTest.java
@@ -22,15 +22,14 @@
import android.net.WifiKey;
import android.os.Parcel;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import java.util.Date;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class TimestampedScoredNetworkTest {
private TimestampedScoredNetwork impl;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java
index 07c50fd..89960cb 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java
@@ -37,19 +37,19 @@
import android.util.ArraySet;
import com.android.settingslib.R;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList;
import java.util.Set;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class WifiUtilsTest {
private static final String TEST_SSID = "\"test_ssid\"";
private static final String TEST_BSSID = "00:00:00:00:00:00";
@@ -79,7 +79,7 @@
Bundle bundle = new Bundle();
ArrayList<ScanResult> scanResults = buildScanResultCache();
bundle.putParcelableArray(AccessPoint.KEY_SCANRESULTS,
- scanResults.toArray(new Parcelable[scanResults.size()]));
+ scanResults.toArray(new Parcelable[0]));
AccessPoint ap = new AccessPoint(mContext, bundle);
when(mockWifiNetworkScoreCache.getScoredNetwork(any(ScanResult.class)))
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java
index 9a5a5b8..be504ef 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java
@@ -23,6 +23,7 @@
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.phone.DozeParameters;
import java.io.PrintWriter;
@@ -80,11 +81,12 @@
if (isAmbientMode != mIsAmbientMode) {
mIsAmbientMode = isAmbientMode;
try {
+ long duration = animated ? StackStateAnimator.ANIMATION_DURATION_WAKEUP : 0L;
if (DEBUG) {
Log.i(TAG, "AOD wallpaper state changed to: " + mIsAmbientMode
- + ", animated: " + animated);
+ + ", animationDuration: " + duration);
}
- mWallpaperManagerService.setInAmbientMode(mIsAmbientMode, animated);
+ mWallpaperManagerService.setInAmbientMode(mIsAmbientMode, duration);
} catch (RemoteException e) {
// Cannot notify wallpaper manager service, but it's fine, let's just skip it.
Log.w(TAG, "Cannot notify state to WallpaperManagerService: " + mIsAmbientMode);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
index 8b434a5..496aa0e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
@@ -323,7 +323,9 @@
post(new Runnable() {
@Override
public void run() {
- handleShowingDetail(detail, x, y, false /* toggleQs */);
+ if (isAttachedToWindow()) {
+ handleShowingDetail(detail, x, y, false /* toggleQs */);
+ }
}
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
index 0638998..3a96595d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
@@ -198,7 +198,8 @@
mIcon.setIcon(state, allowAnimations);
setContentDescription(state.contentDescription);
- mAccessibilityClass = state.expandedAccessibilityClassName;
+ mAccessibilityClass =
+ state.state == Tile.STATE_UNAVAILABLE ? null : state.expandedAccessibilityClassName;
if (state instanceof QSTile.BooleanState) {
boolean newState = ((BooleanState) state).value;
if (mTileState != newState) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java
index 314a31d..0a2e04f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java
@@ -250,23 +250,24 @@
otherChild = null;
}
}
- if (otherChild == null) {
+ if (otherChild == null && previousTranslation < 0) {
+ // Let's fade out as we approach the top of the screen. We can only do this if
+ // we're actually moving up
float distanceToTop = child.getTop() + child.getHeight() + previousTranslation;
transformationAmount = distanceToTop / child.getHeight();
transformationAmount = Math.max(0.0f, Math.min(1.0f, transformationAmount));
- if (to) {
- transformationAmount = 1.0f - transformationAmount;
- }
}
transformView(transformationAmount, to, child, otherChild, false, /* sameAsAny */
useLinearTransformation);
- if (transformationAmount == 0.0f
- && otherGroup.getIsolatedMessage() == otherChild) {
+ boolean otherIsIsolated = otherGroup.getIsolatedMessage() == otherChild;
+ if (transformationAmount == 0.0f && otherIsIsolated) {
ownGroup.setTransformingImages(true);
}
if (otherChild == null) {
child.setTranslationY(previousTranslation);
setClippingDeactivated(child, true);
+ } else if (ownGroup.getIsolatedMessage() == child || otherIsIsolated) {
+ // We don't want to add any translation for the image that is transforming
} else if (to) {
float totalTranslation = child.getTop() + ownGroup.getTop()
- otherChild.getTop() - otherChild.getTop();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 7970f16..1616b6d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -176,9 +176,12 @@
}
// Check if the notification is displaying the menu, if so slide notification back
- if (row.getProvider() != null && row.getProvider().isMenuVisible()) {
+ if (isMenuVisible(row)) {
row.animateTranslateNotification(0);
return;
+ } else if (row.isChildInGroup() && isMenuVisible(row.getNotificationParent())) {
+ row.getNotificationParent().animateTranslateNotification(0);
+ return;
}
// Mark notification for one frame.
@@ -193,6 +196,10 @@
mCallback.onNotificationClicked(sbn, row);
}
+ private boolean isMenuVisible(ExpandableNotificationRow row) {
+ return row.getProvider() != null && row.getProvider().isMenuVisible();
+ }
+
public void register(ExpandableNotificationRow row, StatusBarNotification sbn) {
Notification notification = sbn.getNotification();
if (notification.contentIntent != null || notification.fullScreenIntent != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 0cd431f..d4d45ea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -2067,6 +2067,8 @@
private void setChildIsExpanding(boolean isExpanding) {
mChildIsExpanding = isExpanding;
+ updateClipping();
+ invalidate();
}
@Override
@@ -2968,7 +2970,7 @@
return true;
}
} else if (child == mChildrenContainer) {
- if (!mChildIsExpanding && (isClippingNeeded() || !hasNoRounding())) {
+ if (isClippingNeeded() || !hasNoRounding()) {
return true;
}
} else if (child instanceof NotificationGuts) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
index a7aed5f..0efb130 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
@@ -115,12 +115,14 @@
if (!mCustomOutline) {
int translation = mShouldTranslateContents && !ignoreTranslation
? (int) getTranslation() : 0;
- left = Math.max(translation, 0);
+ int halfExtraWidth = (int) (mExtraWidthForClipping / 2.0f);
+ left = Math.max(translation, 0) - halfExtraWidth;
top = mClipTopAmount + mBackgroundTop;
- right = getWidth() + Math.min(translation, 0);
+ right = getWidth() + halfExtraWidth + Math.min(translation, 0);
// If the top is rounded we want the bottom to be at most at the top roundness, in order
// to avoid the shadow changing when scrolling up.
- bottom = Math.max(getActualHeight() - mClipBottomAmount, (int) (top + topRoundness));
+ bottom = Math.max(mMinimumHeightForClipping,
+ Math.max(getActualHeight() - mClipBottomAmount, (int) (top + topRoundness)));
} else {
left = mOutlineRect.left;
top = mOutlineRect.top;
@@ -219,10 +221,12 @@
public void setExtraWidthForClipping(float extraWidthForClipping) {
mExtraWidthForClipping = extraWidthForClipping;
+ invalidate();
}
public void setMinimumHeightForClipping(int minimumHeightForClipping) {
mMinimumHeightForClipping = minimumHeightForClipping;
+ invalidate();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 2fc3adc..408ab42 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -721,7 +721,7 @@
IWallpaperManager wallpaperManager = IWallpaperManager.Stub.asInterface(
ServiceManager.getService(Context.WALLPAPER_SERVICE));
try {
- wallpaperManager.setInAmbientMode(false /* ambientMode */, false /* animated */);
+ wallpaperManager.setInAmbientMode(false /* ambientMode */, 0L /* duration */);
} catch (RemoteException e) {
// Just pass, nothing critical.
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java
index 6ac4462..ec2319d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java
@@ -16,9 +16,8 @@
package com.android.systemui.doze;
-import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -27,8 +26,8 @@
import android.os.RemoteException;
import android.support.test.filters.SmallTest;
-import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.phone.DozeParameters;
import org.junit.Before;
@@ -59,14 +58,14 @@
mDozeWallpaperState.transitionTo(DozeMachine.State.UNINITIALIZED,
DozeMachine.State.DOZE_AOD);
- verify(mIWallpaperManager).setInAmbientMode(eq(true), anyBoolean());
+ verify(mIWallpaperManager).setInAmbientMode(eq(true), anyLong());
mDozeWallpaperState.transitionTo(DozeMachine.State.DOZE_AOD, DozeMachine.State.FINISH);
- verify(mIWallpaperManager).setInAmbientMode(eq(false), anyBoolean());
+ verify(mIWallpaperManager).setInAmbientMode(eq(false), anyLong());
// Make sure we're sending false when AoD is off
reset(mDozeParameters);
mDozeWallpaperState.transitionTo(DozeMachine.State.FINISH, DozeMachine.State.DOZE_AOD);
- verify(mIWallpaperManager).setInAmbientMode(eq(false), anyBoolean());
+ verify(mIWallpaperManager).setInAmbientMode(eq(false), anyLong());
}
@Test
@@ -78,10 +77,12 @@
mDozeWallpaperState.transitionTo(DozeMachine.State.UNINITIALIZED,
DozeMachine.State.DOZE_AOD);
- verify(mIWallpaperManager).setInAmbientMode(eq(true), eq(true));
+ verify(mIWallpaperManager).setInAmbientMode(eq(true),
+ eq((long) StackStateAnimator.ANIMATION_DURATION_WAKEUP));
mDozeWallpaperState.transitionTo(DozeMachine.State.DOZE_AOD, DozeMachine.State.FINISH);
- verify(mIWallpaperManager).setInAmbientMode(eq(false), eq(true));
+ verify(mIWallpaperManager).setInAmbientMode(eq(false),
+ eq((long) StackStateAnimator.ANIMATION_DURATION_WAKEUP));
}
@Test
@@ -93,24 +94,24 @@
mDozeWallpaperState.transitionTo(DozeMachine.State.UNINITIALIZED,
DozeMachine.State.DOZE_AOD);
- verify(mIWallpaperManager).setInAmbientMode(eq(true), eq(false));
+ verify(mIWallpaperManager).setInAmbientMode(eq(true), eq(0L));
mDozeWallpaperState.transitionTo(DozeMachine.State.DOZE_AOD, DozeMachine.State.FINISH);
- verify(mIWallpaperManager).setInAmbientMode(eq(false), eq(false));
+ verify(mIWallpaperManager).setInAmbientMode(eq(false), eq(0L));
}
@Test
public void testTransitionTo_requestPulseIsAmbientMode() throws RemoteException {
mDozeWallpaperState.transitionTo(DozeMachine.State.DOZE,
DozeMachine.State.DOZE_REQUEST_PULSE);
- verify(mIWallpaperManager).setInAmbientMode(eq(true), eq(false));
+ verify(mIWallpaperManager).setInAmbientMode(eq(true), eq(0L));
}
@Test
public void testTransitionTo_pulseIsAmbientMode() throws RemoteException {
mDozeWallpaperState.transitionTo(DozeMachine.State.DOZE_REQUEST_PULSE,
DozeMachine.State.DOZE_PULSING);
- verify(mIWallpaperManager).setInAmbientMode(eq(true), eq(false));
+ verify(mIWallpaperManager).setInAmbientMode(eq(true), eq(0L));
}
@Test
@@ -120,6 +121,7 @@
reset(mIWallpaperManager);
mDozeWallpaperState.transitionTo(DozeMachine.State.DOZE_PULSING,
DozeMachine.State.FINISH);
- verify(mIWallpaperManager).setInAmbientMode(eq(false), eq(true));
+ verify(mIWallpaperManager).setInAmbientMode(eq(false),
+ eq((long) StackStateAnimator.ANIMATION_DURATION_WAKEUP));
}
}
diff --git a/services/backup/OWNERS b/services/backup/OWNERS
index 645723e..d1dbbff 100644
--- a/services/backup/OWNERS
+++ b/services/backup/OWNERS
@@ -1,7 +1,5 @@
anniemeng@google.com
-artikz@google.com
brufino@google.com
bryanmawhinney@google.com
ctate@google.com
jorlow@google.com
-mkarpinski@google.com
diff --git a/services/core/java/com/android/server/locksettings/SP800Derive.java b/services/core/java/com/android/server/locksettings/SP800Derive.java
new file mode 100644
index 0000000..77561fc
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/SP800Derive.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.locksettings;
+
+import java.nio.ByteBuffer;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+
+/**
+ * Implementation of NIST SP800-108
+ * "Recommendation for Key Derivation Using Pseudorandom Functions"
+ * Hardcoded:
+ * [PRF=HMAC_SHA256]
+ * [CTRLOCATION=BEFORE_FIXED]
+ * [RLEN=32_BITS]
+ * L = 256
+ * L suffix: 32 bits
+ */
+class SP800Derive {
+ private final byte[] mKeyBytes;
+
+ SP800Derive(byte[] keyBytes) {
+ mKeyBytes = keyBytes;
+ }
+
+ private Mac getMac() {
+ try {
+ final Mac m = Mac.getInstance("HmacSHA256");
+ m.init(new SecretKeySpec(mKeyBytes, m.getAlgorithm()));
+ return m;
+ } catch (InvalidKeyException | NoSuchAlgorithmException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static void update32(Mac m, int v) {
+ m.update(ByteBuffer.allocate(Integer.BYTES).putInt(v).array());
+ }
+
+ /**
+ * Generate output from a single, fixed input.
+ */
+ public byte[] fixedInput(byte[] fixedInput) {
+ final Mac m = getMac();
+ update32(m, 1); // Hardwired counter value
+ m.update(fixedInput);
+ return m.doFinal();
+ }
+
+ /**
+ * Generate output from a label and context. We add a length field at the end of the context to
+ * disambiguate it from the length even in the presence of zero bytes.
+ */
+ public byte[] withContext(byte[] label, byte[] context) {
+ final Mac m = getMac();
+ // Hardwired counter value: 1
+ update32(m, 1); // Hardwired counter value
+ m.update(label);
+ m.update((byte) 0);
+ m.update(context);
+ update32(m, context.length * 8); // Disambiguate context
+ update32(m, 256); // Hardwired output length
+ return m.doFinal();
+ }
+}
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
index 596daeb..d32c299 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
@@ -26,9 +26,9 @@
import android.hardware.weaver.V1_0.WeaverReadResponse;
import android.hardware.weaver.V1_0.WeaverReadStatus;
import android.hardware.weaver.V1_0.WeaverStatus;
-import android.security.GateKeeper;
import android.os.RemoteException;
import android.os.UserManager;
+import android.security.GateKeeper;
import android.service.gatekeeper.GateKeeperResponse;
import android.service.gatekeeper.IGateKeeperService;
import android.util.ArrayMap;
@@ -102,7 +102,8 @@
private static final int INVALID_WEAVER_SLOT = -1;
private static final byte SYNTHETIC_PASSWORD_VERSION_V1 = 1;
- private static final byte SYNTHETIC_PASSWORD_VERSION = 2;
+ private static final byte SYNTHETIC_PASSWORD_VERSION_V2 = 2;
+ private static final byte SYNTHETIC_PASSWORD_VERSION_V3 = 3;
private static final byte SYNTHETIC_PASSWORD_PASSWORD_BASED = 0;
private static final byte SYNTHETIC_PASSWORD_TOKEN_BASED = 1;
@@ -128,6 +129,8 @@
private static final byte[] PERSONALISATION_WEAVER_PASSWORD = "weaver-pwd".getBytes();
private static final byte[] PERSONALISATION_WEAVER_KEY = "weaver-key".getBytes();
private static final byte[] PERSONALISATION_WEAVER_TOKEN = "weaver-token".getBytes();
+ private static final byte[] PERSONALISATION_CONTEXT =
+ "android-synthetic-password-personalization-context".getBytes();
static class AuthenticationResult {
public AuthenticationToken authToken;
@@ -136,6 +139,7 @@
}
static class AuthenticationToken {
+ private final byte mVersion;
/*
* Here is the relationship between all three fields:
* P0 and P1 are two randomly-generated blocks. P1 is stored on disk but P0 is not.
@@ -146,29 +150,38 @@
private @Nullable byte[] P1;
private @NonNull String syntheticPassword;
+ AuthenticationToken(byte version) {
+ mVersion = version;
+ }
+
+ private byte[] derivePassword(byte[] personalization) {
+ if (mVersion == SYNTHETIC_PASSWORD_VERSION_V3) {
+ return (new SP800Derive(syntheticPassword.getBytes()))
+ .withContext(personalization, PERSONALISATION_CONTEXT);
+ } else {
+ return SyntheticPasswordCrypto.personalisedHash(personalization,
+ syntheticPassword.getBytes());
+ }
+ }
+
public String deriveKeyStorePassword() {
- return bytesToHex(SyntheticPasswordCrypto.personalisedHash(
- PERSONALIZATION_KEY_STORE_PASSWORD, syntheticPassword.getBytes()));
+ return bytesToHex(derivePassword(PERSONALIZATION_KEY_STORE_PASSWORD));
}
public byte[] deriveGkPassword() {
- return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_SP_GK_AUTH,
- syntheticPassword.getBytes());
+ return derivePassword(PERSONALIZATION_SP_GK_AUTH);
}
public byte[] deriveDiskEncryptionKey() {
- return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_FBE_KEY,
- syntheticPassword.getBytes());
+ return derivePassword(PERSONALIZATION_FBE_KEY);
}
public byte[] deriveVendorAuthSecret() {
- return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_AUTHSECRET_KEY,
- syntheticPassword.getBytes());
+ return derivePassword(PERSONALIZATION_AUTHSECRET_KEY);
}
public byte[] derivePasswordHashFactor() {
- return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_PASSWORD_HASH,
- syntheticPassword.getBytes());
+ return derivePassword(PERSONALIZATION_PASSWORD_HASH);
}
private void initialize(byte[] P0, byte[] P1) {
@@ -185,7 +198,7 @@
}
protected static AuthenticationToken create() {
- AuthenticationToken result = new AuthenticationToken();
+ AuthenticationToken result = new AuthenticationToken(SYNTHETIC_PASSWORD_VERSION_V3);
result.initialize(secureRandom(SYNTHETIC_PASSWORD_LENGTH),
secureRandom(SYNTHETIC_PASSWORD_LENGTH));
return result;
@@ -802,7 +815,16 @@
}
byte[] content = createSPBlob(getHandleName(handle), secret, applicationId, sid);
byte[] blob = new byte[content.length + 1 + 1];
- blob[0] = SYNTHETIC_PASSWORD_VERSION;
+ /*
+ * We can upgrade from v1 to v2 because that's just a change in the way that
+ * the SP is stored. However, we can't upgrade to v3 because that is a change
+ * in the way that passwords are derived from the SP.
+ */
+ if (authToken.mVersion == SYNTHETIC_PASSWORD_VERSION_V3) {
+ blob[0] = SYNTHETIC_PASSWORD_VERSION_V3;
+ } else {
+ blob[0] = SYNTHETIC_PASSWORD_VERSION_V2;
+ }
blob[1] = type;
System.arraycopy(content, 0, blob, 2, content.length);
saveState(SP_BLOB_NAME, blob, handle, userId);
@@ -940,7 +962,9 @@
return null;
}
final byte version = blob[0];
- if (version != SYNTHETIC_PASSWORD_VERSION && version != SYNTHETIC_PASSWORD_VERSION_V1) {
+ if (version != SYNTHETIC_PASSWORD_VERSION_V3
+ && version != SYNTHETIC_PASSWORD_VERSION_V2
+ && version != SYNTHETIC_PASSWORD_VERSION_V1) {
throw new RuntimeException("Unknown blob version");
}
if (blob[1] != type) {
@@ -958,7 +982,7 @@
Log.e(TAG, "Fail to decrypt SP for user " + userId);
return null;
}
- AuthenticationToken result = new AuthenticationToken();
+ AuthenticationToken result = new AuthenticationToken(version);
if (type == SYNTHETIC_PASSWORD_TOKEN_BASED) {
if (!loadEscrowData(result, userId)) {
Log.e(TAG, "User is not escrowable: " + userId);
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 7750c37..0d6dadf 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -554,7 +554,7 @@
final Handler mHandler;
@VisibleForTesting
- public final Handler mUidEventHandler;
+ final Handler mUidEventHandler;
private final ServiceThread mUidEventThread;
@@ -1465,7 +1465,7 @@
}
@VisibleForTesting
- public void updateNetworks() throws InterruptedException {
+ void updateNetworks() throws InterruptedException {
updateNetworksInternal();
final CountDownLatch latch = new CountDownLatch(1);
mHandler.post(() -> {
@@ -1510,7 +1510,7 @@
* @return cycleDay to use in the mobile NetworkPolicy.
*/
@VisibleForTesting
- public int getCycleDayFromCarrierConfig(@Nullable PersistableBundle config,
+ int getCycleDayFromCarrierConfig(@Nullable PersistableBundle config,
int fallbackCycleDay) {
if (config == null) {
return fallbackCycleDay;
@@ -1542,7 +1542,7 @@
* @return warningBytes to use in the mobile NetworkPolicy.
*/
@VisibleForTesting
- public long getWarningBytesFromCarrierConfig(@Nullable PersistableBundle config,
+ long getWarningBytesFromCarrierConfig(@Nullable PersistableBundle config,
long fallbackWarningBytes) {
if (config == null) {
return fallbackWarningBytes;
@@ -1575,7 +1575,7 @@
* @return limitBytes to use in the mobile NetworkPolicy.
*/
@VisibleForTesting
- public long getLimitBytesFromCarrierConfig(@Nullable PersistableBundle config,
+ long getLimitBytesFromCarrierConfig(@Nullable PersistableBundle config,
long fallbackLimitBytes) {
if (config == null) {
return fallbackLimitBytes;
@@ -2039,7 +2039,7 @@
}
@VisibleForTesting
- public NetworkPolicy buildDefaultMobilePolicy(int subId, String subscriberId) {
+ NetworkPolicy buildDefaultMobilePolicy(int subId, String subscriberId) {
final NetworkTemplate template = buildTemplateMobileAll(subscriberId);
final RecurrenceRule cycleRule = NetworkPolicy
.buildRule(ZonedDateTime.now().getDayOfMonth(), ZoneId.systemDefault());
@@ -3489,7 +3489,7 @@
}
@VisibleForTesting
- public boolean isUidForeground(int uid) {
+ boolean isUidForeground(int uid) {
synchronized (mUidRulesFirstLock) {
return isUidStateForeground(
mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY));
@@ -3931,7 +3931,9 @@
* power saving restrictions may still apply.
*/
@VisibleForTesting
- public void setAppIdleWhitelist(int uid, boolean shouldWhitelist) {
+ void setAppIdleWhitelist(int uid, boolean shouldWhitelist) {
+ mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
+
synchronized (mUidRulesFirstLock) {
if (mAppIdleTempWhitelistAppIds.get(uid) == shouldWhitelist) {
// No change.
@@ -3956,7 +3958,7 @@
/** Return the list of UIDs currently in the app idle whitelist. */
@VisibleForTesting
- public int[] getAppIdleWhitelist() {
+ int[] getAppIdleWhitelist() {
mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
synchronized (mUidRulesFirstLock) {
@@ -3971,7 +3973,7 @@
/** Returns if the UID is currently considered idle. */
@VisibleForTesting
- public boolean isUidIdle(int uid) {
+ boolean isUidIdle(int uid) {
synchronized (mUidRulesFirstLock) {
if (mAppIdleTempWhitelistAppIds.get(uid)) {
// UID is temporarily whitelisted.
@@ -4844,13 +4846,13 @@
}
@VisibleForTesting
- public void addIdleHandler(IdleHandler handler) {
+ void addIdleHandler(IdleHandler handler) {
mHandler.getLooper().getQueue().addIdleHandler(handler);
}
@GuardedBy("mUidRulesFirstLock")
@VisibleForTesting
- public void updateRestrictBackgroundByLowPowerModeUL(final PowerSaveState result) {
+ void updateRestrictBackgroundByLowPowerModeUL(final PowerSaveState result) {
mRestrictBackgroundPowerState = result;
boolean restrictBackground = result.batterySaverEnabled;
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index f279af0..94d276c 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -309,6 +309,7 @@
newConfig = mConfig.copy();
ZenRule rule = new ZenRule();
populateZenRule(automaticZenRule, rule, true);
+ newConfig.automaticRules.put(rule.id, rule);
if (setConfigLocked(newConfig, reason, rule.component, true)) {
return rule.id;
} else {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 1045289..c125e97 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -15246,7 +15246,8 @@
"inputs not balanced; missing argument for " + installPackageName);
}
final DeletePackageAction deletePackageAction;
- if (prepareResult.replace) {
+ // we only want to try to delete for non system apps
+ if (prepareResult.replace && !prepareResult.system) {
deletePackageAction = mayDeletePackageLocked(res.removedInfo,
prepareResult.originalPs, prepareResult.disabledPs,
prepareResult.childPackageSettings);
@@ -17818,7 +17819,7 @@
return null;
}
if (isSystemApp(ps)) {
- if (ps.parentPackageName == null) {
+ if (ps.parentPackageName != null) {
Slog.w(TAG, "Attempt to delete child system package " + ps.pkg.packageName);
return null;
}
diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/BasePermission.java
index e194d15..2d583ca3 100644
--- a/services/core/java/com/android/server/pm/permission/BasePermission.java
+++ b/services/core/java/com/android/server/pm/permission/BasePermission.java
@@ -105,6 +105,8 @@
*/
private boolean perUser;
+ boolean usageInfoRequired;
+
public BasePermission(String _name, String _sourcePackageName, @PermissionType int _type) {
name = _name;
sourcePackageName = _sourcePackageName;
@@ -351,6 +353,7 @@
}
if (bp.perm == p) {
bp.protectionLevel = p.info.protectionLevel;
+ bp.usageInfoRequired = p.info.usageInfoRequired;
}
if (PackageManagerService.DEBUG_PACKAGE_SCANNING && r != null) {
Log.d(TAG, " Permissions: " + r);
@@ -430,6 +433,7 @@
permissionInfo.packageName = sourcePackageName;
permissionInfo.nonLocalizedLabel = name;
permissionInfo.protectionLevel = protectionLevel;
+ permissionInfo.usageInfoRequired = usageInfoRequired;
return permissionInfo;
}
@@ -458,6 +462,7 @@
bp.protectionLevel = readInt(parser, null, "protection",
PermissionInfo.PROTECTION_NORMAL);
bp.protectionLevel = PermissionInfo.fixProtectionLevel(bp.protectionLevel);
+ bp.usageInfoRequired = readInt(parser, null, "usageInfoRequired", 0) != 0;
if (dynamic) {
final PermissionInfo pi = new PermissionInfo();
pi.packageName = sourcePackage.intern();
@@ -465,6 +470,7 @@
pi.icon = readInt(parser, null, "icon", 0);
pi.nonLocalizedLabel = parser.getAttributeValue(null, "label");
pi.protectionLevel = bp.protectionLevel;
+ pi.usageInfoRequired = bp.usageInfoRequired;
bp.pendingPermissionInfo = pi;
}
out.put(bp.name, bp);
@@ -497,6 +503,7 @@
if (protectionLevel != PermissionInfo.PROTECTION_NORMAL) {
serializer.attribute(null, "protection", Integer.toString(protectionLevel));
}
+ serializer.attribute(null, "usageInfoRequired", usageInfoRequired ? "1" : "0");
if (type == BasePermission.TYPE_DYNAMIC) {
final PermissionInfo pi = perm != null ? perm.info : pendingPermissionInfo;
if (pi != null) {
@@ -533,6 +540,7 @@
if (!compareStrings(pi1.nonLocalizedLabel, pi2.nonLocalizedLabel)) return false;
// We'll take care of setting this one.
if (!compareStrings(pi1.packageName, pi2.packageName)) return false;
+ if (pi1.usageInfoRequired != pi2.usageInfoRequired) return false;
// These are not currently stored in settings.
//if (!compareStrings(pi1.group, pi2.group)) return false;
//if (!compareStrings(pi1.nonLocalizedDescription, pi2.nonLocalizedDescription)) return false;
@@ -580,6 +588,8 @@
pw.print(" enforced=");
pw.println(readEnforced);
}
+ pw.print(" usageInfoRequired=");
+ pw.println(usageInfoRequired);
return true;
}
}
diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
index 8d27d1e..c8a68b4 100644
--- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
+++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
@@ -402,7 +402,7 @@
throws RemoteException {
try {
final int uid = context.getPackageManager()
- .getPackageUid(packageName, 0);
+ .getPackageUidAsUser(packageName, UserHandle.getCallingUserId());
Preconditions.checkArgument(Binder.getCallingUid() == uid);
} catch (IllegalArgumentException | NullPointerException |
PackageManager.NameNotFoundException e) {
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 6ede423..cfec8ef 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -1181,7 +1181,7 @@
// TODO(multi-display) TBD.
if (mInfo != null && mInfo.supportsAmbientMode() && displayId == DEFAULT_DISPLAY) {
try {
- connector.mEngine.setInAmbientMode(mInAmbientMode, false /* animated */);
+ connector.mEngine.setInAmbientMode(mInAmbientMode, 0L /* duration */);
} catch (RemoteException e) {
Slog.w(TAG, "Failed to set ambient mode state", e);
}
@@ -2023,11 +2023,17 @@
}
}
- // TODO(b/115486823) Extends this method with specific display.
- public void setInAmbientMode(boolean inAmbienMode, boolean animated) {
+ /**
+ * TODO(b/115486823) Extends this method with specific display.
+ * Propagate ambient state to wallpaper engine.
+ *
+ * @param inAmbientMode {@code true} when in ambient mode, {@code false} otherwise.
+ * @param animationDuration Duration of the animation, or 0 when immediate.
+ */
+ public void setInAmbientMode(boolean inAmbientMode, long animationDuration) {
final IWallpaperEngine engine;
synchronized (mLock) {
- mInAmbientMode = inAmbienMode;
+ mInAmbientMode = inAmbientMode;
final WallpaperData data = mWallpaperMap.get(mCurrentUserId);
if (data != null && data.connection != null && data.connection.mInfo != null
&& data.connection.mInfo.supportsAmbientMode()) {
@@ -2040,7 +2046,7 @@
if (engine != null) {
try {
- engine.setInAmbientMode(inAmbienMode, animated);
+ engine.setInAmbientMode(inAmbientMode, animationDuration);
} catch (RemoteException e) {
// Cannot talk to wallpaper engine.
}
@@ -2344,7 +2350,7 @@
return false;
}
if (!android.Manifest.permission.BIND_WALLPAPER.equals(si.permission)) {
- String msg = "Selected service does not require "
+ String msg = "Selected service does not have "
+ android.Manifest.permission.BIND_WALLPAPER
+ ": " + componentName;
if (fromUser) {
@@ -2396,6 +2402,22 @@
}
}
+ if (wi != null && wi.supportsAmbientMode()) {
+ final int hasPrivilege = mIPackageManager.checkPermission(
+ android.Manifest.permission.AMBIENT_WALLPAPER, wi.getPackageName(),
+ serviceUserId);
+ if (hasPrivilege != PackageManager.PERMISSION_GRANTED) {
+ String msg = "Selected service does not have "
+ + android.Manifest.permission.AMBIENT_WALLPAPER
+ + ": " + componentName;
+ if (fromUser) {
+ throw new SecurityException(msg);
+ }
+ Slog.w(TAG, msg);
+ return false;
+ }
+ }
+
// Bind the service!
if (DEBUG) Slog.v(TAG, "Binding to:" + componentName);
final int componentUid = mIPackageManager.getPackageUid(componentName.getPackageName(),
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java b/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java
index e3133ef..eff0f75 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java
@@ -42,7 +42,7 @@
* It must then transition to either {@code CANCELLED} with {@link #onActivityLaunchCancelled}
* or into {@code FINISHED} with {@link #onActivityLaunchFinished}. These are terminal states.
*
- * Note that the {@link ActivityRecord} provided as a parameter to some state transitions isn't
+ * Note that the {@code ActivityRecordProto} provided as a parameter to some state transitions isn't
* necessarily the same within a single launch sequence: it is only the top-most activity at the
* time (if any). Trampoline activities coalesce several activity starts into a single launch
* sequence.
@@ -94,6 +94,14 @@
public static final int TEMPERATURE_HOT = 3;
/**
+ * Typedef marker that a {@code byte[]} actually contains an
+ * <a href="proto/android/server/activitymanagerservice.proto">ActivityRecordProto</a>
+ * in the protobuf format.
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @interface ActivityRecordProto {}
+
+ /**
* Notifies the observer that a new launch sequence has begun as a result of a new intent.
*
* Once a launch sequence begins, the resolved activity will either subsequently start with
@@ -135,7 +143,7 @@
* Multiple calls to this method cannot occur without first terminating the current
* launch sequence.
*/
- public void onActivityLaunched(@NonNull ActivityRecord activity,
+ public void onActivityLaunched(@NonNull @ActivityRecordProto byte[] activity,
@Temperature int temperature);
/**
@@ -157,7 +165,7 @@
* in the case of a trampoline, multiple activities could've been started
* and only the latest activity is reported here.
*/
- public void onActivityLaunchCancelled(@Nullable ActivityRecord abortingActivity);
+ public void onActivityLaunchCancelled(@Nullable @ActivityRecordProto byte[] abortingActivity);
/**
* Notifies the observer that the current launch sequence has been successfully finished.
@@ -178,5 +186,5 @@
* and only the latest activity that was top-most during first-frame drawn
* is reported here.
*/
- public void onActivityLaunchFinished(@NonNull ActivityRecord finalActivity);
+ public void onActivityLaunchFinished(@NonNull @ActivityRecordProto byte[] finalActivity);
}
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserverRegistry.java b/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserverRegistry.java
new file mode 100644
index 0000000..fa90dc5
--- /dev/null
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserverRegistry.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.annotation.NonNull;
+
+/**
+ * Multi-cast delegate implementation for {@link ActivityMetricsLaunchObserver}.
+ *
+ * <br/><br/>
+ * This enables multiple launch observers to subscribe to {@link ActivityMetricsLogger}
+ * independently of each other.
+ *
+ * <br/><br/>
+ * Some callbacks in {@link ActivityMetricsLaunchObserver} have a {@code byte[]}
+ * parameter; this array is reused by all the registered observers, so it must not be written to
+ * (i.e. all observers must treat any array parameters as immutable).
+ *
+ * <br /><br />
+ * Multi-cast invocations occurs sequentially in-order of registered observers.
+ */
+public interface ActivityMetricsLaunchObserverRegistry {
+ /**
+ * Register an extra launch observer to receive the multi-cast.
+ *
+ * <br /><br />
+ * Multi-cast invocation happens in the same order the observers were registered. For example,
+ * <pre>
+ * registerLaunchObserver(A)
+ * registerLaunchObserver(B)
+ *
+ * obs.onIntentFailed() ->
+ * A.onIntentFailed()
+ * B.onIntentFailed()
+ * </pre>
+ */
+ void registerLaunchObserver(@NonNull ActivityMetricsLaunchObserver launchObserver);
+
+ /**
+ * Unregister an existing launch observer. It will not receive the multi-cast in the future.
+ *
+ * <br /><br />
+ * This does nothing if this observer was not already registered.
+ */
+ void unregisterLaunchObserver(@NonNull ActivityMetricsLaunchObserver launchObserver);
+}
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 416e133..16df52d 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -99,10 +99,12 @@
import android.util.SparseIntArray;
import android.util.StatsLog;
import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.os.BackgroundThread;
import com.android.internal.os.SomeArgs;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
/**
@@ -168,7 +170,8 @@
* Due to the global single concurrent launch sequence, all calls to this observer must be made
* in-order on the same thread to fulfill the "happens-before" guarantee in LaunchObserver.
*/
- private final ActivityMetricsLaunchObserver mLaunchObserver = null;
+ private final LaunchObserverRegistryImpl mLaunchObserver;
+ @VisibleForTesting static final int LAUNCH_OBSERVER_ACTIVITY_RECORD_PROTO_CHUNK_SIZE = 512;
private final class H extends Handler {
@@ -263,6 +266,7 @@
mSupervisor = supervisor;
mContext = context;
mHandler = new H(looper);
+ mLaunchObserver = new LaunchObserverRegistryImpl(looper);
}
void logWindowState() {
@@ -1000,12 +1004,19 @@
}
}
+ public ActivityMetricsLaunchObserverRegistry getLaunchObserverRegistry() {
+ return mLaunchObserver;
+ }
+
/** Notify the {@link ActivityMetricsLaunchObserver} that a new launch sequence has begun. */
private void launchObserverNotifyIntentStarted(Intent intent) {
- if (mLaunchObserver != null) {
- // Beginning a launch is timing sensitive and so should be observed as soon as possible.
- mLaunchObserver.onIntentStarted(intent);
- }
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ "MetricsLogger:launchObserverNotifyIntentStarted");
+
+ // Beginning a launch is timing sensitive and so should be observed as soon as possible.
+ mLaunchObserver.onIntentStarted(intent);
+
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
/**
@@ -1014,9 +1025,12 @@
* intent being delivered to the top running activity.
*/
private void launchObserverNotifyIntentFailed() {
- if (mLaunchObserver != null) {
- mLaunchObserver.onIntentFailed();
- }
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ "MetricsLogger:launchObserverNotifyIntentFailed");
+
+ mLaunchObserver.onIntentFailed();
+
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
/**
@@ -1024,14 +1038,17 @@
* has started.
*/
private void launchObserverNotifyActivityLaunched(WindowingModeTransitionInfo info) {
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ "MetricsLogger:launchObserverNotifyActivityLaunched");
+
@ActivityMetricsLaunchObserver.Temperature int temperature =
convertTransitionTypeToLaunchObserverTemperature(getTransitionType(info));
- if (mLaunchObserver != null) {
- // Beginning a launch is timing sensitive and so should be observed as soon as possible.
- mLaunchObserver.onActivityLaunched(info.launchedActivity,
- temperature);
- }
+ // Beginning a launch is timing sensitive and so should be observed as soon as possible.
+ mLaunchObserver.onActivityLaunched(convertActivityRecordToProto(info.launchedActivity),
+ temperature);
+
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
/**
@@ -1039,11 +1056,15 @@
* cancelled.
*/
private void launchObserverNotifyActivityLaunchCancelled(WindowingModeTransitionInfo info) {
- final ActivityRecord launchedActivity = info != null ? info.launchedActivity : null;
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ "MetricsLogger:launchObserverNotifyActivityLaunchCancelled");
- if (mLaunchObserver != null) {
- mLaunchObserver.onActivityLaunchCancelled(launchedActivity);
- }
+ final @ActivityMetricsLaunchObserver.ActivityRecordProto byte[] activityRecordProto =
+ info != null ? convertActivityRecordToProto(info.launchedActivity) : null;
+
+ mLaunchObserver.onActivityLaunchCancelled(activityRecordProto);
+
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
/**
@@ -1051,11 +1072,34 @@
* has fully finished (successfully).
*/
private void launchObserverNotifyActivityLaunchFinished(WindowingModeTransitionInfo info) {
- final ActivityRecord launchedActivity = info.launchedActivity;
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ "MetricsLogger:launchObserverNotifyActivityLaunchFinished");
- if (mLaunchObserver != null) {
- mLaunchObserver.onActivityLaunchFinished(launchedActivity);
- }
+ mLaunchObserver.onActivityLaunchFinished(
+ convertActivityRecordToProto(info.launchedActivity));
+
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ }
+
+ @VisibleForTesting
+ static @ActivityMetricsLaunchObserver.ActivityRecordProto byte[]
+ convertActivityRecordToProto(ActivityRecord record) {
+ // May take non-negligible amount of time to convert ActivityRecord into a proto,
+ // so track the time.
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ "MetricsLogger:convertActivityRecordToProto");
+
+ // There does not appear to be a way to 'reset' a ProtoOutputBuffer stream,
+ // so create a new one every time.
+ final ProtoOutputStream protoOutputStream =
+ new ProtoOutputStream(LAUNCH_OBSERVER_ACTIVITY_RECORD_PROTO_CHUNK_SIZE);
+ // Write this data out as the top-most ActivityRecordProto (i.e. it is not a sub-object).
+ record.writeToProto(protoOutputStream);
+ final byte[] bytes = protoOutputStream.getBytes();
+
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+
+ return bytes;
}
private static @ActivityMetricsLaunchObserver.Temperature int
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index b4aec35..6f2461b 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -3215,8 +3215,11 @@
proto.end(token);
}
- public void writeToProto(ProtoOutputStream proto, long fieldId) {
- final long token = proto.start(fieldId);
+ /**
+ * Write all fields to an {@code ActivityRecordProto}. This assumes the
+ * {@code ActivityRecordProto} is the outer-most proto data.
+ */
+ void writeToProto(ProtoOutputStream proto) {
super.writeToProto(proto, CONFIGURATION_CONTAINER, false /* trim */);
writeIdentifierToProto(proto, IDENTIFIER);
proto.write(STATE, mState.toString());
@@ -3226,6 +3229,11 @@
proto.write(PROC_ID, app.getPid());
}
proto.write(TRANSLUCENT, !fullscreen);
+ }
+
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ writeToProto(proto);
proto.end(token);
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 3162ee3..987c706 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -434,7 +434,9 @@
mInitialized = true;
mRunningTasks = createRunningTasks();
- mActivityMetricsLogger = new ActivityMetricsLogger(this, mService.mContext, mHandler.getLooper());
+
+ mActivityMetricsLogger = new ActivityMetricsLogger(this, mService.mContext,
+ mHandler.getLooper());
mKeyguardController = new KeyguardController(mService, this);
mPersisterQueue = new PersisterQueue();
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index d665592..0cdbedb 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -473,4 +473,6 @@
public abstract void setProfileApp(String profileApp);
public abstract void setProfileProc(WindowProcessController wpc);
public abstract void setProfilerInfo(ProfilerInfo profilerInfo);
+
+ public abstract ActivityMetricsLaunchObserverRegistry getLaunchObserverRegistry();
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 8f99dae..e1a1e61 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -4511,6 +4511,21 @@
return mKeyguardController.isKeyguardLocked();
}
+ /**
+ * Clears launch params for the given package.
+ * @param packageNames the names of the packages of which the launch params are to be cleared
+ */
+ @Override
+ public void clearLaunchParamsForPackages(List<String> packageNames) {
+ mAmInternal.enforceCallingPermission(Manifest.permission.MANAGE_ACTIVITY_STACKS,
+ "clearLaunchParamsForPackages");
+ synchronized (mGlobalLock) {
+ for (int i = 0; i < packageNames.size(); ++i) {
+ mStackSupervisor.mLaunchParamsPersister.removeRecordForPackage(packageNames.get(i));
+ }
+ }
+ }
+
void dumpLastANRLocked(PrintWriter pw) {
pw.println("ACTIVITY MANAGER LAST ANR (dumpsys activity lastanr)");
if (mLastANRState == null) {
@@ -6879,5 +6894,12 @@
mProfilerInfo = profilerInfo;
}
}
+
+ @Override
+ public ActivityMetricsLaunchObserverRegistry getLaunchObserverRegistry() {
+ synchronized (mGlobalLock) {
+ return mStackSupervisor.getActivityMetricsLogger().getLaunchObserverRegistry();
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index f1d1e49..7aabc15 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -21,6 +21,7 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.content.ContentResolver;
import android.content.Context;
@@ -30,6 +31,7 @@
import android.content.res.Resources;
import android.database.ContentObserver;
import android.hardware.power.V1_0.PowerHint;
+import android.net.Uri;
import android.os.Handler;
import android.os.SystemProperties;
import android.os.UserHandle;
@@ -57,6 +59,7 @@
private final WindowManagerService mService;
private final DisplayContent mDisplayContent;
private final DisplayPolicy mDisplayPolicy;
+ private final DisplayWindowSettings mDisplayWindowSettings;
private final Context mContext;
private final Object mLock;
@@ -71,10 +74,6 @@
private StatusBarManagerInternal mStatusBarManagerInternal;
private SettingsObserver mSettingsObserver;
- // Default display does not rotate, apps that require non-default orientation will have to
- // have the orientation emulated.
- private boolean mForceDefaultOrientation;
-
private int mCurrentAppOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
@VisibleForTesting
@@ -93,6 +92,13 @@
private int mUserRotationMode = WindowManagerPolicy.USER_ROTATION_FREE;
private int mUserRotation = Surface.ROTATION_0;
+ /**
+ * A flag to indicate if the display rotation should be fixed to user specified rotation
+ * regardless of all other states (including app requrested orientation). {@code true} the
+ * display rotation should be fixed to user specified rotation, {@code false} otherwise.
+ */
+ private boolean mFixedToUserRotation;
+
private int mDemoHdmiRotation;
private int mDemoRotation;
private boolean mDemoHdmiRotationLock;
@@ -100,15 +106,17 @@
DisplayRotation(WindowManagerService service, DisplayContent displayContent) {
this(service, displayContent, displayContent.getDisplayPolicy(),
- service.mContext, service.getWindowManagerLock());
+ service.mDisplayWindowSettings, service.mContext, service.getWindowManagerLock());
}
@VisibleForTesting
DisplayRotation(WindowManagerService service, DisplayContent displayContent,
- DisplayPolicy displayPolicy, Context context, Object lock) {
+ DisplayPolicy displayPolicy, DisplayWindowSettings displayWindowSettings,
+ Context context, Object lock) {
mService = service;
mDisplayContent = displayContent;
mDisplayPolicy = displayPolicy;
+ mDisplayWindowSettings = displayWindowSettings;
mContext = context;
mLock = lock;
isDefaultDisplay = displayContent.isDefaultDisplay;
@@ -204,12 +212,19 @@
// so if the orientation is forced, we need to respect that no matter what.
final boolean isTv = mContext.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_LEANBACK);
- mForceDefaultOrientation = ((longSizeDp >= 960 && shortSizeDp >= 720) || isCar || isTv) &&
- res.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation) &&
- // For debug purposes the next line turns this feature off with:
- // $ adb shell setprop config.override_forced_orient true
- // $ adb shell wm size reset
- !"true".equals(SystemProperties.get("config.override_forced_orient"));
+ final boolean forceDefaultOrientationInRes =
+ res.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation);
+ final boolean forceDefaultOrienation =
+ ((longSizeDp >= 960 && shortSizeDp >= 720) || isCar || isTv)
+ && forceDefaultOrientationInRes
+ // For debug purposes the next line turns this feature off with:
+ // $ adb shell setprop config.override_forced_orient true
+ // $ adb shell wm size reset
+ && !"true".equals(SystemProperties.get("config.override_forced_orient"));
+ // Configuration says we force to use the default orientation. We can fall back to fix
+ // rotation to only user rotation. As long as OEM doesn't change user rotation then the
+ // rotation of this display is effectively stuck at 0 deg.
+ setFixedToUserRotation(forceDefaultOrienation);
}
void setRotation(int rotation) {
@@ -227,7 +242,14 @@
}
}
- void restoreUserRotation(int userRotationMode, int userRotation) {
+ void restoreSettings(int userRotationMode, int userRotation,
+ boolean fixedToUserRotation) {
+ mFixedToUserRotation = fixedToUserRotation;
+
+ // We will retrieve user rotation and user rotation mode from settings for default display.
+ if (isDefaultDisplay) {
+ return;
+ }
if (userRotationMode != WindowManagerPolicy.USER_ROTATION_FREE
&& userRotationMode != WindowManagerPolicy.USER_ROTATION_LOCKED) {
Slog.w(TAG, "Trying to restore an invalid user rotation mode " + userRotationMode
@@ -243,6 +265,18 @@
mUserRotation = userRotation;
}
+ void setFixedToUserRotation(boolean fixedToUserRotation) {
+ if (mFixedToUserRotation == fixedToUserRotation) {
+ return;
+ }
+
+ mFixedToUserRotation = fixedToUserRotation;
+ mDisplayWindowSettings.setFixedToUserRotation(mDisplayContent,
+ fixedToUserRotation);
+ mService.updateRotation(true /* alwaysSendConfiguration */,
+ false /* forceRelayout */);
+ }
+
private void setUserRotation(int userRotationMode, int userRotation) {
if (isDefaultDisplay) {
// We'll be notified via settings listener, so we don't need to update internal values.
@@ -265,7 +299,7 @@
mUserRotation = userRotation;
changed = true;
}
- mService.mDisplayWindowSettings.setUserRotation(mDisplayContent, userRotationMode,
+ mDisplayWindowSettings.setUserRotation(mDisplayContent, userRotationMode,
userRotation);
if (changed) {
mService.updateRotation(true /* alwaysSendConfiguration */,
@@ -291,9 +325,8 @@
Settings.System.ACCELEROMETER_ROTATION, 0, UserHandle.USER_CURRENT) == 0;
}
- /** @return true if com.android.internal.R.bool#config_forceDefaultOrientation is true. */
- boolean isDefaultOrientationForced() {
- return mForceDefaultOrientation;
+ boolean isFixedToUserRotation() {
+ return mFixedToUserRotation;
}
public int getLandscapeRotation() {
@@ -399,6 +432,12 @@
* screen is switched off.
*/
private boolean needSensorRunning() {
+ if (mFixedToUserRotation) {
+ // We are sure we only respect user rotation settings, so we are sure we will not
+ // support sensor rotation.
+ return false;
+ }
+
if (mSupportAutoRotation) {
if (mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR
|| mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
@@ -459,8 +498,8 @@
);
}
- if (mForceDefaultOrientation) {
- return Surface.ROTATION_0;
+ if (mFixedToUserRotation) {
+ return mUserRotation;
}
int sensorRotation = mOrientationListener != null
@@ -701,8 +740,8 @@
// demo, hdmi, vr, etc mode.
// Determine if the rotation is currently forced.
- if (mForceDefaultOrientation) {
- return false; // Rotation is forced to default orientation.
+ if (mFixedToUserRotation) {
+ return false; // Rotation is forced to user settings.
}
final int lidState = mDisplayPolicy.getLidState();
@@ -861,6 +900,7 @@
pw.print(" mDemoHdmiRotationLock=" + mDemoHdmiRotationLock);
pw.println(" mUndockedHdmiRotation=" + Surface.rotationToString(mUndockedHdmiRotation));
pw.println(prefix + " mLidOpenRotation=" + Surface.rotationToString(mLidOpenRotation));
+ pw.println(prefix + " mFixedToUserRotation=" + mFixedToUserRotation);
}
private class OrientationListener extends WindowOrientationListener {
@@ -945,4 +985,10 @@
}
}
}
+
+ @VisibleForTesting
+ interface ContentObserverRegister {
+ void registerContentObserver(Uri uri, boolean notifyForDescendants,
+ ContentObserver observer, @UserIdInt int userHandle);
+ }
}
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
index f7dfd3f..45d77de 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
@@ -80,6 +80,7 @@
private boolean mShouldShowWithInsecureKeyguard = false;
private boolean mShouldShowSystemDecors = false;
private boolean mShouldShowIme = false;
+ private boolean mFixedToUserRotation;
private Entry(String name) {
mName = name;
@@ -97,7 +98,8 @@
&& mRemoveContentMode == REMOVE_CONTENT_MODE_UNDEFINED
&& !mShouldShowWithInsecureKeyguard
&& !mShouldShowSystemDecors
- && !mShouldShowIme;
+ && !mShouldShowIme
+ && !mFixedToUserRotation;
}
}
@@ -186,6 +188,13 @@
writeSettingsIfNeeded(entry, displayInfo);
}
+ void setFixedToUserRotation(DisplayContent displayContent, boolean fixedToUserRotation) {
+ final DisplayInfo displayInfo = displayContent.getDisplayInfo();
+ final Entry entry = getOrCreateEntry(displayInfo);
+ entry.mFixedToUserRotation = fixedToUserRotation;
+ writeSettingsIfNeeded(entry, displayInfo);
+ }
+
private int getWindowingModeLocked(Entry entry, int displayId) {
int windowingMode = entry != null ? entry.mWindowingMode
: WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -331,7 +340,8 @@
displayInfo.overscanRight = entry.mOverscanRight;
displayInfo.overscanBottom = entry.mOverscanBottom;
- dc.getDisplayRotation().restoreUserRotation(entry.mUserRotationMode, entry.mUserRotation);
+ dc.getDisplayRotation().restoreSettings(entry.mUserRotationMode,
+ entry.mUserRotation, entry.mFixedToUserRotation);
if (entry.mForcedDensity != 0) {
dc.mBaseDisplayDensity = entry.mForcedDensity;
@@ -458,6 +468,8 @@
"shouldShowWithInsecureKeyguard");
entry.mShouldShowSystemDecors = getBooleanAttribute(parser, "shouldShowSystemDecors");
entry.mShouldShowIme = getBooleanAttribute(parser, "shouldShowIme");
+ entry.mFixedToUserRotation = getBooleanAttribute(parser,
+ "fixedToUserRotation");
mEntries.put(name, entry);
}
XmlUtils.skipCurrentTag(parser);
@@ -541,6 +553,10 @@
if (entry.mShouldShowIme) {
out.attribute(null, "shouldShowIme", Boolean.toString(entry.mShouldShowIme));
}
+ if (entry.mFixedToUserRotation) {
+ out.attribute(null, "fixedToUserRotation",
+ Boolean.toString(entry.mFixedToUserRotation));
+ }
out.endTag(null, "display");
}
diff --git a/services/core/java/com/android/server/wm/LaunchObserverRegistryImpl.java b/services/core/java/com/android/server/wm/LaunchObserverRegistryImpl.java
new file mode 100644
index 0000000..93e2d8d
--- /dev/null
+++ b/services/core/java/com/android/server/wm/LaunchObserverRegistryImpl.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import android.content.Intent;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+
+import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.function.pooled.PooledLambda;
+
+import java.util.ArrayList;
+
+/**
+ * Multi-cast implementation of {@link ActivityMetricsLaunchObserver}.
+ *
+ * <br /><br />
+ * If this class is called through the {@link ActivityMetricsLaunchObserver} interface,
+ * then the call is forwarded to all registered observers at the time.
+ *
+ * <br /><br />
+ * All calls are invoked asynchronously in-order on a background thread. This fulfills the
+ * sequential ordering guarantee in {@link ActivityMetricsLaunchObserverRegistry}.
+ *
+ * @see ActivityTaskManagerInternal#getLaunchObserverRegistry()
+ */
+class LaunchObserverRegistryImpl implements
+ ActivityMetricsLaunchObserverRegistry, ActivityMetricsLaunchObserver {
+ private final ArrayList<ActivityMetricsLaunchObserver> mList = new ArrayList<>();
+
+ /**
+ * All calls are posted to a handler because:
+ *
+ * 1. We don't know how long the observer will take to handle this call and we don't want
+ * to block the WM critical section on it.
+ * 2. We don't know the lock ordering of the observer so we don't want to expose a chance
+ * of deadlock.
+ */
+ private final Handler mHandler;
+
+ public LaunchObserverRegistryImpl(Looper looper) {
+ mHandler = new Handler(looper);
+ }
+
+ @Override
+ public void registerLaunchObserver(ActivityMetricsLaunchObserver launchObserver) {
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ LaunchObserverRegistryImpl::handleRegisterLaunchObserver, this, launchObserver));
+ }
+
+ @Override
+ public void unregisterLaunchObserver(ActivityMetricsLaunchObserver launchObserver) {
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ LaunchObserverRegistryImpl::handleUnregisterLaunchObserver, this, launchObserver));
+ }
+
+ @Override
+ public void onIntentStarted(Intent intent) {
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ LaunchObserverRegistryImpl::handleOnIntentStarted, this, intent));
+ }
+
+ @Override
+ public void onIntentFailed() {
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ LaunchObserverRegistryImpl::handleOnIntentFailed, this));
+ }
+
+ @Override
+ public void onActivityLaunched(
+ @ActivityRecordProto byte[] activity,
+ int temperature) {
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ LaunchObserverRegistryImpl::handleOnActivityLaunched,
+ this, activity, temperature));
+ }
+
+ @Override
+ public void onActivityLaunchCancelled(
+ @ActivityRecordProto byte[] activity) {
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ LaunchObserverRegistryImpl::handleOnActivityLaunchCancelled, this, activity));
+ }
+
+ @Override
+ public void onActivityLaunchFinished(
+ @ActivityRecordProto byte[] activity) {
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ LaunchObserverRegistryImpl::handleOnActivityLaunchFinished, this, activity));
+ }
+
+ // Use PooledLambda.obtainMessage to invoke below methods. Every method reference must be
+ // unbound (i.e. not capture any variables explicitly or implicitly) to fulfill the
+ // singleton-lambda requirement.
+
+ private void handleRegisterLaunchObserver(ActivityMetricsLaunchObserver observer) {
+ mList.add(observer);
+ }
+
+ private void handleUnregisterLaunchObserver(ActivityMetricsLaunchObserver observer) {
+ mList.remove(observer);
+ }
+
+ private void handleOnIntentStarted(Intent intent) {
+ // Traverse start-to-end to meet the registerLaunchObserver multi-cast order guarantee.
+ for (int i = 0; i < mList.size(); i++) {
+ ActivityMetricsLaunchObserver o = mList.get(i);
+ o.onIntentStarted(intent);
+ }
+ }
+
+ private void handleOnIntentFailed() {
+ // Traverse start-to-end to meet the registerLaunchObserver multi-cast order guarantee.
+ for (int i = 0; i < mList.size(); i++) {
+ ActivityMetricsLaunchObserver o = mList.get(i);
+ o.onIntentFailed();
+ }
+ }
+
+ private void handleOnActivityLaunched(
+ @ActivityRecordProto byte[] activity,
+ @Temperature int temperature) {
+ // Traverse start-to-end to meet the registerLaunchObserver multi-cast order guarantee.
+ for (int i = 0; i < mList.size(); i++) {
+ ActivityMetricsLaunchObserver o = mList.get(i);
+ o.onActivityLaunched(activity, temperature);
+ }
+ }
+
+ private void handleOnActivityLaunchCancelled(
+ @ActivityRecordProto byte[] activity) {
+ // Traverse start-to-end to meet the registerLaunchObserver multi-cast order guarantee.
+ for (int i = 0; i < mList.size(); i++) {
+ ActivityMetricsLaunchObserver o = mList.get(i);
+ o.onActivityLaunchCancelled(activity);
+ }
+ }
+
+ private void handleOnActivityLaunchFinished(
+ @ActivityRecordProto byte[] activity) {
+ // Traverse start-to-end to meet the registerLaunchObserver multi-cast order guarantee.
+ for (int i = 0; i < mList.size(); i++) {
+ ActivityMetricsLaunchObserver o = mList.get(i);
+ o.onActivityLaunchFinished(activity);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/LaunchParamsPersister.java b/services/core/java/com/android/server/wm/LaunchParamsPersister.java
index da9a507..bc6a690 100644
--- a/services/core/java/com/android/server/wm/LaunchParamsPersister.java
+++ b/services/core/java/com/android/server/wm/LaunchParamsPersister.java
@@ -269,7 +269,7 @@
outParams.mBounds.set(persistableParams.mBounds);
}
- private void onPackageRemoved(String packageName) {
+ void removeRecordForPackage(String packageName) {
final List<File> fileToDelete = new ArrayList<>();
for (int i = 0; i < mMap.size(); ++i) {
int userId = mMap.keyAt(i);
@@ -310,7 +310,7 @@
@Override
public void onPackageRemoved(String packageName) {
- LaunchParamsPersister.this.onPackageRemoved(packageName);
+ removeRecordForPackage(packageName);
}
}
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index ee0e89b..3947bd4 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -222,7 +222,7 @@
}
public ScreenRotationAnimation(Context context, DisplayContent displayContent,
- boolean forceDefaultOrientation, boolean isSecure, WindowManagerService service) {
+ boolean fixedToUserRotation, boolean isSecure, WindowManagerService service) {
mService = service;
mContext = context;
mDisplayContent = displayContent;
@@ -234,7 +234,7 @@
final int originalWidth;
final int originalHeight;
DisplayInfo displayInfo = displayContent.getDisplayInfo();
- if (forceDefaultOrientation) {
+ if (fixedToUserRotation) {
// Emulated orientation.
mForceDefaultOrientation = true;
originalWidth = displayContent.mBaseDisplayWidth;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index b91afcd..4085f3d 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -3591,6 +3591,17 @@
}
}
+ void setRotateForApp(int displayId, boolean enabled) {
+ synchronized (mGlobalLock) {
+ final DisplayContent display = mRoot.getDisplayContent(displayId);
+ if (display == null) {
+ Slog.w(TAG, "Trying to set rotate for app for a missing display.");
+ return;
+ }
+ display.getDisplayRotation().setFixedToUserRotation(enabled);
+ }
+ }
+
@Override
public void freezeRotation(int rotation) {
freezeDisplayRotation(Display.DEFAULT_DISPLAY, rotation);
@@ -5397,7 +5408,7 @@
displayContent.updateDisplayInfo();
screenRotationAnimation = new ScreenRotationAnimation(mContext, displayContent,
- displayContent.getDisplayRotation().isDefaultOrientationForced(), isSecure,
+ displayContent.getDisplayRotation().isFixedToUserRotation(), isSecure,
this);
mAnimator.setScreenRotationAnimationLocked(mFrozenDisplayId,
screenRotationAnimation);
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index bf77ba8..6865ce3 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -76,6 +76,8 @@
getNextArgRequired());
case "set-user-rotation":
return runSetDisplayUserRotation(pw);
+ case "set-fix-to-user-rotation":
+ return runSetFixToUserRotation(pw);
default:
return handleDefaultCommands(cmd);
}
@@ -297,6 +299,32 @@
}
}
+ private int runSetFixToUserRotation(PrintWriter pw) {
+ int displayId = Display.DEFAULT_DISPLAY;
+ String arg = getNextArgRequired();
+ if ("-d".equals(arg)) {
+ displayId = Integer.parseInt(getNextArgRequired());
+ arg = getNextArgRequired();
+ }
+
+ final boolean enabled;
+ switch (arg) {
+ case "enabled":
+ enabled = true;
+ break;
+ case "disabled":
+ enabled = false;
+ break;
+ default:
+ getErrPrintWriter().println("Error: expecting enabled or disabled, but we get "
+ + arg);
+ return -1;
+ }
+
+ mInternal.setRotateForApp(displayId, enabled);
+ return 0;
+ }
+
@Override
public void onHelp() {
PrintWriter pw = getOutPrintWriter();
@@ -316,6 +344,8 @@
pw.println(" Dismiss the keyguard, prompting user for auth if necessary.");
pw.println(" set-user-rotation [free|lock] [-d DISPLAY_ID] [rotation]");
pw.println(" Set user rotation mode and user rotation.");
+ pw.println(" set-fix-to-user-rotation [-d DISPLAY_ID] [enabled|disabled]");
+ pw.println(" Enable or disable rotating display for app requested orientation.");
if (!IS_USER) {
pw.println(" tracing (start | stop)");
pw.println(" Start or stop window tracing.");
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/SP800DeriveTests.java b/services/tests/servicestests/src/com/android/server/locksettings/SP800DeriveTests.java
new file mode 100644
index 0000000..fc2dcb9
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locksettings/SP800DeriveTests.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.locksettings;
+
+import android.test.AndroidTestCase;
+
+import com.android.internal.util.HexDump;
+
+public class SP800DeriveTests extends AndroidTestCase {
+ public void testFixedInput() throws Exception {
+ // CAVP: https://csrc.nist.gov/projects/cryptographic-algorithm-validation-program/key-derivation
+ byte[] keyBytes = HexDump.hexStringToByteArray(
+ "e204d6d466aad507ffaf6d6dab0a5b26"
+ + "152c9e21e764370464e360c8fbc765c6");
+ SP800Derive sk = new SP800Derive(keyBytes);
+ byte[] fixedInput = HexDump.hexStringToByteArray(
+ "7b03b98d9f94b899e591f3ef264b71b1"
+ + "93fba7043c7e953cde23bc5384bc1a62"
+ + "93580115fae3495fd845dadbd02bd645"
+ + "5cf48d0f62b33e62364a3a80");
+ byte[] res = sk.fixedInput(fixedInput);
+ assertEquals((
+ "770dfab6a6a4a4bee0257ff335213f78"
+ + "d8287b4fd537d5c1fffa956910e7c779").toUpperCase(), HexDump.toHexString(res));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
rename to services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index 92efc3c..99b827c 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server;
+package com.android.server.net;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.TYPE_WIFI;
@@ -72,7 +72,6 @@
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isA;
-import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
@@ -142,9 +141,8 @@
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.internal.util.test.BroadcastInterceptingContext.FutureIntent;
-import com.android.server.net.NetworkPolicyManagerInternal;
-import com.android.server.net.NetworkPolicyManagerService;
-import com.android.server.net.NetworkStatsManagerInternal;
+import com.android.server.DeviceIdleController;
+import com.android.server.LocalServices;
import com.google.common.util.concurrent.AbstractFuture;
@@ -195,15 +193,6 @@
/**
* Tests for {@link NetworkPolicyManagerService}.
- *
- * <p>Typical usage:
- *
- * <pre><code>
- m -j32 FrameworksServicesTests && adb install -r -g \
- ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk && \
- adb shell am instrument -e class "com.android.server.NetworkPolicyManagerServiceTest" -w \
- "com.android.frameworks.servicestests/androidx.test.runner.AndroidJUnitRunner"
- * </code></pre>
*/
@RunWith(AndroidJUnit4.class)
@MediumTest
@@ -376,7 +365,7 @@
return null;
}
}).when(mActivityManager).registerUidObserver(any(), anyInt(),
- eq(NetworkPolicyManager.FOREGROUND_THRESHOLD_STATE), isNull(String.class));
+ eq(NetworkPolicyManager.FOREGROUND_THRESHOLD_STATE), any(String.class));
mFutureIntent = newRestrictBackgroundChangedFuture();
mService = new NetworkPolicyManagerService(mServiceContext, mActivityManager,
@@ -425,7 +414,7 @@
// catch INetworkManagementEventObserver during systemReady()
final ArgumentCaptor<INetworkManagementEventObserver> networkObserver =
- ArgumentCaptor.forClass(INetworkManagementEventObserver.class);
+ ArgumentCaptor.forClass(INetworkManagementEventObserver.class);
verify(mNetworkManager).registerObserver(networkObserver.capture());
mNetworkObserver = networkObserver.getValue();
@@ -1782,7 +1771,7 @@
}
private static NetworkPolicy buildFakeMobilePolicy(int cycleDay, long warningBytes,
- long limitBytes, boolean inferred){
+ long limitBytes, boolean inferred) {
final NetworkTemplate template = buildTemplateMobileAll(FAKE_SUBSCRIBER_ID);
return new NetworkPolicy(template, cycleDay, new Time().timezone, warningBytes,
limitBytes, SNOOZE_NEVER, SNOOZE_NEVER, true, inferred);
@@ -1880,7 +1869,7 @@
}
private static void assertNotificationType(int expected, String actualTag) {
- assertEquals("notification type mismatch for '" + actualTag +"'",
+ assertEquals("notification type mismatch for '" + actualTag + "'",
Integer.toString(expected), actualTag.substring(actualTag.lastIndexOf(':') + 1));
}
@@ -1914,7 +1903,8 @@
final String action = ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED;
final Intent intent = future.get(5, TimeUnit.SECONDS);
assertNotNull("Didn't get a " + action + "intent in 5 seconds");
- assertEquals("Wrong package on " + action + " intent", expectedPackage, intent.getPackage());
+ assertEquals("Wrong package on " + action + " intent",
+ expectedPackage, intent.getPackage());
}
// TODO: replace by Truth, Hamcrest, or a similar tool.
@@ -1935,7 +1925,7 @@
}
if (errors.length() > 0) {
fail("assertContainsInAnyOrder(expected=" + Arrays.toString(expected)
- + ", actual=" + Arrays.toString(actual) +") failed: \n" + errors);
+ + ", actual=" + Arrays.toString(actual) + ") failed: \n" + errors);
}
}
@@ -1998,7 +1988,7 @@
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
- Log.d(TAG,"counting down on answer: " + invocation);
+ Log.d(TAG, "counting down on answer: " + invocation);
latch.countDown();
return null;
}
@@ -2036,8 +2026,8 @@
final String assetPath = NETPOLICY_DIR + "/" + mNetpolicyXml;
final File netConfigFile = new File(mPolicyDir, "netpolicy.xml");
Log.d(TAG, "Creating " + netConfigFile + " from asset " + assetPath);
- try (final InputStream in = context.getResources().getAssets().open(assetPath);
- final OutputStream out = new FileOutputStream(netConfigFile)) {
+ try (InputStream in = context.getResources().getAssets().open(assetPath);
+ OutputStream out = new FileOutputStream(netConfigFile)) {
Streams.copy(in, out);
}
}
@@ -2049,9 +2039,7 @@
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface NetPolicyXml {
-
- public String value() default "";
-
+ String value() default "";
}
/**
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index d798865..ce59e6e 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -31,6 +31,7 @@
import android.content.pm.ServiceInfo;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.Signature;
+import android.content.pm.UsesPermissionInfo;
import android.os.Bundle;
import android.os.Parcel;
import android.platform.test.annotations.Presubmit;
@@ -464,6 +465,7 @@
pkg.services.add(new PackageParser.Service(dummy, new ServiceInfo()));
pkg.instrumentation.add(new PackageParser.Instrumentation(dummy, new InstrumentationInfo()));
pkg.requestedPermissions.add("foo7");
+ pkg.usesPermissionInfos.add(new UsesPermissionInfo("foo7"));
pkg.implicitPermissions.add("foo25");
pkg.protectedBroadcasts = new ArrayList<>();
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayWindowSettingsTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayWindowSettingsTests.java
index b823e70..7f390a5 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DisplayWindowSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DisplayWindowSettingsTests.java
@@ -27,7 +27,12 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.app.WindowConfiguration;
import android.platform.test.annotations.Presubmit;
@@ -378,6 +383,33 @@
mSecondaryDisplay.getDisplayRotation().getUserRotation());
}
+ @Test
+ public void testNotFixedToUserRotationByDefault() {
+ mTarget.setUserRotation(mPrimaryDisplay, WindowManagerPolicy.USER_ROTATION_LOCKED,
+ Surface.ROTATION_0);
+
+ final DisplayRotation displayRotation = mock(DisplayRotation.class);
+ mPrimaryDisplay = spy(mPrimaryDisplay);
+ when(mPrimaryDisplay.getDisplayRotation()).thenReturn(displayRotation);
+
+ mTarget.applySettingsToDisplayLocked(mPrimaryDisplay);
+
+ verify(displayRotation).restoreSettings(anyInt(), anyInt(), eq(false));
+ }
+
+ @Test
+ public void testSetFixedToUserRotation() {
+ mTarget.setFixedToUserRotation(mPrimaryDisplay, true);
+
+ final DisplayRotation displayRotation = mock(DisplayRotation.class);
+ mPrimaryDisplay = spy(mPrimaryDisplay);
+ when(mPrimaryDisplay.getDisplayRotation()).thenReturn(displayRotation);
+
+ applySettingsToDisplayByNewInstance(mPrimaryDisplay);
+
+ verify(displayRotation).restoreSettings(anyInt(), anyInt(), eq(true));
+ }
+
private static void assertOverscan(DisplayContent display, int left, int top, int right,
int bottom) {
final DisplayInfo info = display.getDisplayInfo();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 38d8e39..6c7ede3 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -41,6 +41,7 @@
import android.app.AppGlobals;
import android.app.AppOpsManager;
+import android.app.AutomaticZenRule;
import android.app.NotificationManager;
import android.app.NotificationManager.Policy;
import android.content.ComponentName;
@@ -1097,6 +1098,25 @@
assertFalse(Objects.equals(defaultRuleName, ruleAfterUpdating.name)); // update name
}
+ @Test
+ public void testAddAutomaticZenRule() {
+ AutomaticZenRule zenRule = new AutomaticZenRule("name",
+ new ComponentName("android", "ScheduleConditionProvider"),
+ ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
+ NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
+ String id = mZenModeHelperSpy.addAutomaticZenRule(zenRule, "test");
+
+ assertTrue(id != null);
+ ZenModeConfig.ZenRule ruleInConfig = mZenModeHelperSpy.mConfig.automaticRules.get(id);
+ assertTrue(ruleInConfig != null);
+ assertEquals(zenRule.isEnabled(), ruleInConfig.enabled);
+ assertEquals(zenRule.isModified(), ruleInConfig.modified);
+ assertEquals(zenRule.getConditionId(), ruleInConfig.conditionId);
+ assertEquals(NotificationManager.zenModeFromInterruptionFilter(
+ zenRule.getInterruptionFilter(), -1), ruleInConfig.zenMode);
+ assertEquals(zenRule.getName(), ruleInConfig.name);
+ }
+
private void setupZenConfig() {
mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
mZenModeHelperSpy.mConfig.allowAlarms = false;
diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml
index f128b4e2..67acec6 100644
--- a/services/tests/wmtests/AndroidManifest.xml
+++ b/services/tests/wmtests/AndroidManifest.xml
@@ -37,6 +37,8 @@
<application android:debuggable="true"
android:testOnly="true">
+ <uses-library android:name="android.test.mock" android:required="true" />
+
<activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityA" />
<activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityB" />
<activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityRequestedOrientationChange" />
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
index 0e30037..cac9cf6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -24,20 +24,30 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyNoMoreInteractions;
+import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.timeout;
import android.content.Intent;
import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;
import android.util.SparseIntArray;
+import android.util.proto.ProtoOutputStream;
import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
+import com.android.server.wm.ActivityMetricsLaunchObserver.ActivityRecordProto;
+
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
+import org.mockito.ArgumentMatcher;
+
+import java.util.Arrays;
/**
* Tests for the {@link ActivityMetricsLaunchObserver} class.
@@ -51,6 +61,7 @@
public class ActivityMetricsLaunchObserverTests extends ActivityTestsBase {
private ActivityMetricsLogger mActivityMetricsLogger;
private ActivityMetricsLaunchObserver mLaunchObserver;
+ private ActivityMetricsLaunchObserverRegistry mLaunchObserverRegistry;
private TestActivityStack mStack;
private TaskRecord mTask;
@@ -61,16 +72,13 @@
public void setUpAMLO() throws Exception {
setupActivityTaskManagerService();
- mActivityMetricsLogger =
- new ActivityMetricsLogger(mSupervisor, mService.mContext, mService.mH.getLooper());
-
mLaunchObserver = mock(ActivityMetricsLaunchObserver.class);
- // TODO: Use ActivityMetricsLaunchObserverRegistry .
- java.lang.reflect.Field f =
- mActivityMetricsLogger.getClass().getDeclaredField("mLaunchObserver");
- f.setAccessible(true);
- f.set(mActivityMetricsLogger, mLaunchObserver);
+ // ActivityStackSupervisor always creates its own instance of ActivityMetricsLogger.
+ mActivityMetricsLogger = mSupervisor.getActivityMetricsLogger();
+
+ mLaunchObserverRegistry = mActivityMetricsLogger.getLaunchObserverRegistry();
+ mLaunchObserverRegistry.registerLaunchObserver(mLaunchObserver);
// Sometimes we need an ActivityRecord for ActivityMetricsLogger to do anything useful.
// This seems to be the easiest way to create an ActivityRecord.
@@ -81,13 +89,45 @@
mActivityRecordTrampoline = new ActivityBuilder(mService).setTask(mTask).build();
}
+ @After
+ public void tearDownAMLO() throws Exception {
+ if (mLaunchObserverRegistry != null) { // Don't NPE if setUp failed.
+ mLaunchObserverRegistry.unregisterLaunchObserver(mLaunchObserver);
+ }
+ }
+
+ static class ActivityRecordMatcher implements ArgumentMatcher</*@ActivityRecordProto*/ byte[]> {
+ private final @ActivityRecordProto byte[] mExpected;
+
+ public ActivityRecordMatcher(ActivityRecord activityRecord) {
+ mExpected = activityRecordToProto(activityRecord);
+ }
+
+ public boolean matches(@ActivityRecordProto byte[] actual) {
+ return Arrays.equals(mExpected, actual);
+ }
+ }
+
+ static @ActivityRecordProto byte[] activityRecordToProto(ActivityRecord record) {
+ return ActivityMetricsLogger.convertActivityRecordToProto(record);
+ }
+
+ static @ActivityRecordProto byte[] eqProto(ActivityRecord record) {
+ return argThat(new ActivityRecordMatcher(record));
+ }
+
+ static <T> T verifyAsync(T mock) {
+ // AMLO callbacks happen on a separate thread than AML calls, so we need to use a timeout.
+ return verify(mock, timeout(100));
+ }
+
@Test
public void testOnIntentStarted() throws Exception {
Intent intent = new Intent("action 1");
mActivityMetricsLogger.notifyActivityLaunching(intent);
- verify(mLaunchObserver).onIntentStarted(eq(intent));
+ verifyAsync(mLaunchObserver).onIntentStarted(eq(intent));
verifyNoMoreInteractions(mLaunchObserver);
}
@@ -102,7 +142,7 @@
mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT,
activityRecord);
- verify(mLaunchObserver).onIntentFailed();
+ verifyAsync(mLaunchObserver).onIntentFailed();
verifyNoMoreInteractions(mLaunchObserver);
}
@@ -113,7 +153,7 @@
mActivityMetricsLogger.notifyActivityLaunched(START_SUCCESS,
mActivityRecord);
- verify(mLaunchObserver).onActivityLaunched(eq(mActivityRecord), anyInt());
+ verifyAsync(mLaunchObserver).onActivityLaunched(eqProto(mActivityRecord), anyInt());
verifyNoMoreInteractions(mLaunchObserver);
}
@@ -127,7 +167,7 @@
mActivityMetricsLogger.notifyWindowsDrawn(mActivityRecord.getWindowingMode(),
SystemClock.uptimeMillis());
- verify(mLaunchObserver).onActivityLaunchFinished(eq(mActivityRecord));
+ verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(mActivityRecord));
verifyNoMoreInteractions(mLaunchObserver);
}
@@ -135,12 +175,12 @@
public void testOnActivityLaunchCancelled() throws Exception {
testOnActivityLaunched();
- mActivityRecord.nowVisible = true;
+ mActivityRecord.mDrawn = true;
// Cannot time already-visible activities.
mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT, mActivityRecord);
- verify(mLaunchObserver).onActivityLaunchCancelled(eq(mActivityRecord));
+ verifyAsync(mLaunchObserver).onActivityLaunchCancelled(eqProto(mActivityRecord));
verifyNoMoreInteractions(mLaunchObserver);
}
@@ -151,7 +191,7 @@
mActivityMetricsLogger.notifyActivityLaunched(START_SUCCESS,
mActivityRecord);
- verify(mLaunchObserver).onActivityLaunched(eq(mActivityRecord), anyInt());
+ verifyAsync(mLaunchObserver).onActivityLaunched(eqProto(mActivityRecord), anyInt());
// A second, distinct, activity launch is coalesced into the the current app launch sequence
mActivityMetricsLogger.notifyActivityLaunched(START_SUCCESS,
@@ -170,7 +210,7 @@
mActivityMetricsLogger.notifyWindowsDrawn(mActivityRecordTrampoline.getWindowingMode(),
SystemClock.uptimeMillis());
- verify(mLaunchObserver).onActivityLaunchFinished(eq(mActivityRecordTrampoline));
+ verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(mActivityRecordTrampoline));
verifyNoMoreInteractions(mLaunchObserver);
}
@@ -178,13 +218,26 @@
public void testOnActivityLaunchCancelledTrampoline() throws Exception {
testOnActivityLaunchedTrampoline();
- mActivityRecordTrampoline.nowVisible = true;
+ mActivityRecordTrampoline.mDrawn = true;
// Cannot time already-visible activities.
mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT,
mActivityRecordTrampoline);
- verify(mLaunchObserver).onActivityLaunchCancelled(eq(mActivityRecordTrampoline));
+ verifyAsync(mLaunchObserver).onActivityLaunchCancelled(eqProto(mActivityRecordTrampoline));
verifyNoMoreInteractions(mLaunchObserver);
}
+
+ @Test
+ public void testActivityRecordProtoIsNotTooBig() throws Exception {
+ // The ActivityRecordProto must not be too big, otherwise converting it at runtime
+ // will become prohibitively expensive.
+ assertWithMessage("mActivityRecord: %s", mActivityRecord).
+ that(activityRecordToProto(mActivityRecord).length).
+ isAtMost(ActivityMetricsLogger.LAUNCH_OBSERVER_ACTIVITY_RECORD_PROTO_CHUNK_SIZE);
+
+ assertWithMessage("mActivityRecordTrampoline: %s", mActivityRecordTrampoline).
+ that(activityRecordToProto(mActivityRecordTrampoline).length).
+ isAtMost(ActivityMetricsLogger.LAUNCH_OBSERVER_ACTIVITY_RECORD_PROTO_CHUNK_SIZE);
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
new file mode 100644
index 0000000..e988994
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
@@ -0,0 +1,823 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.atMost;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.same;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.os.PowerManagerInternal;
+import android.os.SystemClock;
+import android.platform.test.annotations.Presubmit;
+import android.provider.Settings;
+import android.view.Surface;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.server.LocalServices;
+import com.android.server.UiThread;
+import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.statusbar.StatusBarManagerInternal;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Test class for {@link DisplayRotation}.
+ *
+ * Build/Install/Run:
+ * atest WmTests:DisplayRotationTests
+ */
+@SmallTest
+@Presubmit
+@FlakyTest(detail = "Confirm stable in post-submit before removing")
+public class DisplayRotationTests {
+ private static final long UI_HANDLER_WAIT_TIMEOUT_MS = 50;
+
+ private StatusBarManagerInternal mPreviousStatusBarManagerInternal;
+
+ private WindowManagerService mMockWm;
+ private DisplayContent mMockDisplayContent;
+ private DisplayPolicy mMockDisplayPolicy;
+ private Context mMockContext;
+ private Resources mMockRes;
+ private SensorManager mMockSensorManager;
+ private Sensor mFakeSensor;
+ private DisplayWindowSettings mMockDisplayWindowSettings;
+ private ContentResolver mMockResolver;
+ private FakeSettingsProvider mFakeSettingsProvider;
+ private StatusBarManagerInternal mMockStatusBarManagerInternal;
+
+ // Fields below are callbacks captured from test target.
+ private ContentObserver mShowRotationSuggestionsObserver;
+ private ContentObserver mAccelerometerRotationObserver;
+ private ContentObserver mUserRotationObserver;
+ private SensorEventListener mOrientationSensorListener;
+
+ private DisplayRotationBuilder mBuilder;
+
+ private DisplayRotation mTarget;
+
+ @Before
+ public void setUp() {
+ FakeSettingsProvider.clearSettingsProvider();
+
+ mMockWm = mock(WindowManagerService.class);
+ mMockWm.mPowerManagerInternal = mock(PowerManagerInternal.class);
+
+ mPreviousStatusBarManagerInternal = LocalServices.getService(
+ StatusBarManagerInternal.class);
+ LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
+ mMockStatusBarManagerInternal = mock(StatusBarManagerInternal.class);
+ LocalServices.addService(StatusBarManagerInternal.class, mMockStatusBarManagerInternal);
+
+ mBuilder = new DisplayRotationBuilder();
+ }
+
+ @After
+ public void tearDown() {
+ LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
+ if (mPreviousStatusBarManagerInternal != null) {
+ LocalServices.addService(StatusBarManagerInternal.class,
+ mPreviousStatusBarManagerInternal);
+ mPreviousStatusBarManagerInternal = null;
+ }
+ }
+
+ // ================================
+ // Display Settings Related Tests
+ // ================================
+ @Test
+ public void testLocksUserRotation_LockRotation_DefaultDisplay() throws Exception {
+ mBuilder.build();
+
+ freezeRotation(Surface.ROTATION_180);
+
+ assertEquals(WindowManagerPolicy.USER_ROTATION_LOCKED, mTarget.getUserRotationMode());
+ assertEquals(Surface.ROTATION_180, mTarget.getUserRotation());
+
+ assertEquals(0, Settings.System.getInt(mMockResolver,
+ Settings.System.ACCELEROMETER_ROTATION));
+ assertEquals(Surface.ROTATION_180, Settings.System.getInt(mMockResolver,
+ Settings.System.USER_ROTATION));
+ }
+
+ @Test
+ public void testPersistsUserRotation_LockRotation_NonDefaultDisplay() throws Exception {
+ mBuilder.mIsDefaultDisplay = false;
+
+ mBuilder.build();
+
+ freezeRotation(Surface.ROTATION_180);
+
+ assertEquals(WindowManagerPolicy.USER_ROTATION_LOCKED, mTarget.getUserRotationMode());
+ assertEquals(Surface.ROTATION_180, mTarget.getUserRotation());
+
+ verify(mMockDisplayWindowSettings).setUserRotation(mMockDisplayContent,
+ WindowManagerPolicy.USER_ROTATION_LOCKED, Surface.ROTATION_180);
+ }
+
+ @Test
+ public void testPersistUserRotation_UnlockRotation_DefaultDisplay() throws Exception {
+ mBuilder.build();
+
+ thawRotation();
+
+ assertEquals(WindowManagerPolicy.USER_ROTATION_FREE, mTarget.getUserRotationMode());
+
+ assertEquals(1, Settings.System.getInt(mMockResolver,
+ Settings.System.ACCELEROMETER_ROTATION));
+ }
+
+ @Test
+ public void testPersistsUserRotation_UnlockRotation_NonDefaultDisplay() throws Exception {
+ mBuilder.mIsDefaultDisplay = false;
+
+ mBuilder.build();
+
+ thawRotation();
+
+ assertEquals(WindowManagerPolicy.USER_ROTATION_FREE, mTarget.getUserRotationMode());
+
+ verify(mMockDisplayWindowSettings).setUserRotation(same(mMockDisplayContent),
+ eq(WindowManagerPolicy.USER_ROTATION_FREE), anyInt());
+ }
+
+ @Test
+ public void testPersistsFixedToUserRotation() throws Exception {
+ mBuilder.build();
+
+ mTarget.setFixedToUserRotation(true);
+
+ verify(mMockDisplayWindowSettings).setFixedToUserRotation(mMockDisplayContent, true);
+
+ reset(mMockDisplayWindowSettings);
+ mTarget.setFixedToUserRotation(false);
+
+ verify(mMockDisplayWindowSettings).setFixedToUserRotation(mMockDisplayContent, false);
+ }
+
+ // ========================================
+ // Tests for User Rotation based Rotation
+ // ========================================
+ @Test
+ public void testReturnsUserRotation_UserRotationLocked_NoAppRequest()
+ throws Exception {
+ mBuilder.build();
+ configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
+
+ freezeRotation(Surface.ROTATION_180);
+
+ assertEquals(Surface.ROTATION_180, mTarget.rotationForOrientation(
+ ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_90));
+ }
+
+ @Test
+ public void testReturnsUserRotation_UserRotationLocked_CompatibleAppRequest()
+ throws Exception {
+ mBuilder.build();
+ configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
+
+ freezeRotation(Surface.ROTATION_180);
+
+ assertEquals(Surface.ROTATION_180, mTarget.rotationForOrientation(
+ ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, Surface.ROTATION_90));
+ }
+
+ @Test
+ public void testReturnsSidesays_UserRotationLocked_IncompatibleAppRequest()
+ throws Exception {
+ mBuilder.build();
+ configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
+
+ freezeRotation(Surface.ROTATION_180);
+
+ final int rotation = mTarget.rotationForOrientation(
+ ActivityInfo.SCREEN_ORIENTATION_PORTRAIT, Surface.ROTATION_90);
+ assertTrue("Rotation should be sideways, but it's "
+ + Surface.rotationToString(rotation),
+ rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270);
+ }
+
+ // =================================
+ // Tests for Sensor based Rotation
+ // =================================
+ private void verifyOrientationListenerRegistration(int numOfInvocation) {
+ final ArgumentCaptor<SensorEventListener> listenerCaptor = ArgumentCaptor.forClass(
+ SensorEventListener.class);
+ verify(mMockSensorManager, times(numOfInvocation)).registerListener(
+ listenerCaptor.capture(),
+ same(mFakeSensor),
+ anyInt(),
+ any());
+ if (numOfInvocation > 0) {
+ mOrientationSensorListener = listenerCaptor.getValue();
+ }
+ }
+
+ @Test
+ public void testNotEnablesSensor_AutoRotationNotSupported() throws Exception {
+ mBuilder.setSupportAutoRotation(false).build();
+ configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+
+ thawRotation();
+
+ when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true);
+ when(mMockDisplayPolicy.isAwake()).thenReturn(true);
+ when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true);
+ when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true);
+ mTarget.updateOrientationListener();
+ verifyOrientationListenerRegistration(0);
+ }
+
+ @Test
+ public void testNotEnablesSensor_ScreenNotOn() throws Exception {
+ mBuilder.build();
+ configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+
+ thawRotation();
+
+ when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(false);
+ when(mMockDisplayPolicy.isAwake()).thenReturn(true);
+ when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true);
+ when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true);
+ mTarget.updateOrientationListener();
+ verifyOrientationListenerRegistration(0);
+ }
+
+ @Test
+ public void testNotEnablesSensor_NotAwake() throws Exception {
+ mBuilder.build();
+ configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+
+ thawRotation();
+
+ when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true);
+ when(mMockDisplayPolicy.isAwake()).thenReturn(false);
+ when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true);
+ when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true);
+ mTarget.updateOrientationListener();
+ verifyOrientationListenerRegistration(0);
+ }
+
+ @Test
+ public void testNotEnablesSensor_KeyguardNotDrawnCompletely() throws Exception {
+ mBuilder.build();
+ configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+
+ thawRotation();
+
+ when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true);
+ when(mMockDisplayPolicy.isAwake()).thenReturn(true);
+ when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(false);
+ when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true);
+ mTarget.updateOrientationListener();
+ verifyOrientationListenerRegistration(0);
+ }
+
+ @Test
+ public void testNotEnablesSensor_WindowManagerNotDrawnCompletely() throws Exception {
+ mBuilder.build();
+ configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+
+ thawRotation();
+
+ when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true);
+ when(mMockDisplayPolicy.isAwake()).thenReturn(true);
+ when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true);
+ when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(false);
+ mTarget.updateOrientationListener();
+ verifyOrientationListenerRegistration(0);
+ }
+
+ @Test
+ public void testNotEnablesSensor_FixedUserRotation() throws Exception {
+ mBuilder.build();
+ configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+
+ when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true);
+ when(mMockDisplayPolicy.isAwake()).thenReturn(true);
+ when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true);
+ when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true);
+ mTarget.setFixedToUserRotation(true);
+ mTarget.updateOrientationListener();
+ verifyOrientationListenerRegistration(0);
+ }
+
+ @Test
+ public void testNotEnablesSensor_ForceDefaultRotation() throws Exception {
+ mBuilder.build();
+ when(mMockRes.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation))
+ .thenReturn(true);
+ configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
+
+ when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true);
+ when(mMockDisplayPolicy.isAwake()).thenReturn(true);
+ when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true);
+ when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true);
+ mTarget.updateOrientationListener();
+ verifyOrientationListenerRegistration(0);
+ }
+
+ @Test
+ public void testNotEnablesSensor_ForceDefaultRotation_Car() throws Exception {
+ mBuilder.build();
+ when(mMockRes.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation))
+ .thenReturn(true);
+ configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, true, false);
+
+ when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true);
+ when(mMockDisplayPolicy.isAwake()).thenReturn(true);
+ when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true);
+ when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true);
+ mTarget.updateOrientationListener();
+ verifyOrientationListenerRegistration(0);
+ }
+
+ @Test
+ public void testNotEnablesSensor_ForceDefaultRotation_Tv() throws Exception {
+ mBuilder.build();
+ when(mMockRes.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation))
+ .thenReturn(true);
+ configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, true);
+
+ when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true);
+ when(mMockDisplayPolicy.isAwake()).thenReturn(true);
+ when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true);
+ when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true);
+ mTarget.updateOrientationListener();
+ verifyOrientationListenerRegistration(0);
+ }
+
+ private void enableOrientationSensor() {
+ when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true);
+ when(mMockDisplayPolicy.isAwake()).thenReturn(true);
+ when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true);
+ when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true);
+ mTarget.updateOrientationListener();
+ verifyOrientationListenerRegistration(1);
+ }
+
+ private SensorEvent createSensorEvent(int rotation) throws Exception {
+ final Constructor<SensorEvent> constructor =
+ SensorEvent.class.getDeclaredConstructor(int.class);
+ constructor.setAccessible(true);
+ final SensorEvent event = constructor.newInstance(1);
+ event.sensor = mFakeSensor;
+ event.values[0] = rotation;
+ event.timestamp = SystemClock.elapsedRealtimeNanos();
+ return event;
+ }
+
+ @Test
+ public void testReturnsSensorRotation_RotationThawed() throws Exception {
+ mBuilder.build();
+ configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+
+ thawRotation();
+
+ enableOrientationSensor();
+
+ mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90));
+
+ assertEquals(Surface.ROTATION_90, mTarget.rotationForOrientation(
+ SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
+ }
+
+ private boolean waitForUiHandler() throws Exception {
+ final CountDownLatch latch = new CountDownLatch(1);
+ UiThread.getHandler().post(latch::countDown);
+ return latch.await(UI_HANDLER_WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ }
+
+ @Test
+ public void testUpdatesRotationWhenSensorUpdates_RotationThawed() throws Exception {
+ mBuilder.build();
+ configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+
+ thawRotation();
+
+ enableOrientationSensor();
+
+ mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90));
+ assertTrue(waitForUiHandler());
+
+ verify(mMockWm).updateRotation(false, false);
+ }
+
+ @Test
+ public void testNotifiesChoiceWhenSensorUpdates_RotationLocked() throws Exception {
+ mBuilder.build();
+ configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+
+ freezeRotation(Surface.ROTATION_270);
+
+ enableOrientationSensor();
+
+ mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90));
+ assertTrue(waitForUiHandler());
+
+ verify(mMockStatusBarManagerInternal).onProposedRotationChanged(Surface.ROTATION_90, true);
+ }
+
+ @Test
+ public void testReturnsCompatibleRotation_SensorEnabled_RotationThawed() throws Exception {
+ mBuilder.build();
+ configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+
+ thawRotation();
+
+ enableOrientationSensor();
+
+ mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_180));
+
+ final int rotation = mTarget.rotationForOrientation(SCREEN_ORIENTATION_LANDSCAPE,
+ Surface.ROTATION_0);
+ assertTrue("Rotation should be sideways but it's "
+ + Surface.rotationToString(rotation),
+ rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270);
+ }
+
+ @Test
+ public void testReturnsUserRotation_SensorEnabled_RotationLocked() throws Exception {
+ mBuilder.build();
+ configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+
+ freezeRotation(Surface.ROTATION_270);
+
+ enableOrientationSensor();
+
+ mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_180));
+
+ assertEquals(Surface.ROTATION_270, mTarget.rotationForOrientation(
+ SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
+ }
+
+ // =================================
+ // Tests for Policy based Rotation
+ // =================================
+ @Test
+ public void testReturnsUserRotation_ForceDefaultRotation() throws Exception {
+ mBuilder.build();
+ when(mMockRes.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation))
+ .thenReturn(true);
+ configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
+
+ assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation(SCREEN_ORIENTATION_PORTRAIT,
+ Surface.ROTATION_180));
+ }
+
+ @Test
+ public void testReturnsUserRotation_ForceDefaultRotation_Car() throws Exception {
+ mBuilder.build();
+ when(mMockRes.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation))
+ .thenReturn(true);
+ configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, true, false);
+
+ assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation(SCREEN_ORIENTATION_PORTRAIT,
+ Surface.ROTATION_180));
+ }
+
+ @Test
+ public void testReturnsUserRotation_ForceDefaultRotation_Tv() throws Exception {
+ mBuilder.build();
+ when(mMockRes.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation))
+ .thenReturn(true);
+ configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, true);
+
+ assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation(SCREEN_ORIENTATION_PORTRAIT,
+ Surface.ROTATION_180));
+ }
+
+ @Test
+ public void testReturnsLidOpenRotation_LidOpen() throws Exception {
+ mBuilder.setLidOpenRotation(Surface.ROTATION_90).build();
+ configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
+
+ when(mMockDisplayPolicy.getLidState()).thenReturn(
+ WindowManagerPolicy.WindowManagerFuncs.LID_OPEN);
+
+ freezeRotation(Surface.ROTATION_270);
+
+ assertEquals(Surface.ROTATION_90, mTarget.rotationForOrientation(
+ SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
+ }
+
+ @Test
+ public void testReturnsCarDockRotation_CarDockedMode() throws Exception {
+ mBuilder.setCarDockRotation(Surface.ROTATION_270).build();
+ configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
+
+ when(mMockDisplayPolicy.getDockMode()).thenReturn(Intent.EXTRA_DOCK_STATE_CAR);
+
+ freezeRotation(Surface.ROTATION_90);
+
+ assertEquals(Surface.ROTATION_270, mTarget.rotationForOrientation(
+ SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_90));
+ }
+
+ @Test
+ public void testReturnsDeskDockRotation_DeskDockedMode() throws Exception {
+ mBuilder.setDeskDockRotation(Surface.ROTATION_270).build();
+ configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
+
+ when(mMockDisplayPolicy.getDockMode()).thenReturn(Intent.EXTRA_DOCK_STATE_DESK);
+
+ freezeRotation(Surface.ROTATION_90);
+
+ assertEquals(Surface.ROTATION_270, mTarget.rotationForOrientation(
+ SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_90));
+ }
+
+ @Test
+ public void testReturnsUserRotation_FixedToUserRotation_IgnoreIncompatibleAppRequest()
+ throws Exception {
+ mBuilder.build();
+ configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+
+ mTarget.setFixedToUserRotation(true);
+
+ freezeRotation(Surface.ROTATION_180);
+
+ final int rotation = mTarget.rotationForOrientation(
+ ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, Surface.ROTATION_90);
+ assertEquals(Surface.ROTATION_180, rotation);
+ }
+
+ @Test
+ public void testReturnsUserRotation_NonDefaultDisplay() throws Exception {
+ mBuilder.setIsDefaultDisplay(false).build();
+ configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
+
+ freezeRotation(Surface.ROTATION_90);
+
+ assertEquals(Surface.ROTATION_90, mTarget.rotationForOrientation(
+ SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
+ }
+
+ /**
+ * Call {@link DisplayRotation#configure(int, int, int, int)} to configure {@link #mTarget}
+ * according to given parameters.
+ */
+ private void configureDisplayRotation(int displayOrientation, boolean isCar, boolean isTv) {
+ final int width;
+ final int height;
+ switch (displayOrientation) {
+ case SCREEN_ORIENTATION_LANDSCAPE:
+ width = 1920;
+ height = 1080;
+ break;
+ case SCREEN_ORIENTATION_PORTRAIT:
+ width = 1080;
+ height = 1920;
+ break;
+ default:
+ throw new IllegalArgumentException("displayOrientation needs to be either landscape"
+ + " or portrait, but we got "
+ + ActivityInfo.screenOrientationToString(displayOrientation));
+ }
+
+ final PackageManager mockPackageManager = mock(PackageManager.class);
+ when(mMockContext.getPackageManager()).thenReturn(mockPackageManager);
+ when(mockPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE))
+ .thenReturn(isCar);
+ when(mockPackageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK))
+ .thenReturn(isTv);
+
+ final int shortSizeDp = (isCar || isTv) ? 540 : 720;
+ final int longSizeDp = 960;
+ mTarget.configure(width, height, shortSizeDp, longSizeDp);
+ }
+
+ private void freezeRotation(int rotation) {
+ mTarget.freezeRotation(rotation);
+
+ if (mTarget.isDefaultDisplay) {
+ mAccelerometerRotationObserver.onChange(false);
+ mUserRotationObserver.onChange(false);
+ }
+ }
+
+ private void thawRotation() {
+ mTarget.thawRotation();
+
+ if (mTarget.isDefaultDisplay) {
+ mAccelerometerRotationObserver.onChange(false);
+ mUserRotationObserver.onChange(false);
+ }
+ }
+
+ private class DisplayRotationBuilder {
+ private boolean mIsDefaultDisplay = true;
+ private boolean mSupportAutoRotation = true;
+
+ private int mLidOpenRotation = WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT;
+ private int mCarDockRotation;
+ private int mDeskDockRotation;
+ private int mUndockedHdmiRotation;
+
+ private DisplayRotationBuilder setIsDefaultDisplay(boolean isDefaultDisplay) {
+ mIsDefaultDisplay = isDefaultDisplay;
+ return this;
+ }
+
+ private DisplayRotationBuilder setSupportAutoRotation(boolean supportAutoRotation) {
+ mSupportAutoRotation = supportAutoRotation;
+ return this;
+ }
+
+ private DisplayRotationBuilder setLidOpenRotation(int rotation) {
+ mLidOpenRotation = rotation;
+ return this;
+ }
+
+ private DisplayRotationBuilder setCarDockRotation(int rotation) {
+ mCarDockRotation = rotation;
+ return this;
+ }
+
+ private DisplayRotationBuilder setDeskDockRotation(int rotation) {
+ mDeskDockRotation = rotation;
+ return this;
+ }
+
+ private DisplayRotationBuilder setUndockedHdmiRotation(int rotation) {
+ mUndockedHdmiRotation = rotation;
+ return this;
+ }
+
+ private void captureObservers() {
+ ArgumentCaptor<ContentObserver> captor = ArgumentCaptor.forClass(
+ ContentObserver.class);
+ verify(mMockResolver, atMost(1)).registerContentObserver(
+ eq(Settings.Secure.getUriFor(Settings.Secure.SHOW_ROTATION_SUGGESTIONS)),
+ anyBoolean(),
+ captor.capture(),
+ anyInt());
+ if (!captor.getAllValues().isEmpty()) {
+ mShowRotationSuggestionsObserver = captor.getValue();
+ }
+
+ captor = ArgumentCaptor.forClass(ContentObserver.class);
+ verify(mMockResolver, atMost(1)).registerContentObserver(
+ eq(Settings.System.getUriFor(Settings.System.ACCELEROMETER_ROTATION)),
+ anyBoolean(),
+ captor.capture(),
+ anyInt());
+ if (!captor.getAllValues().isEmpty()) {
+ mAccelerometerRotationObserver = captor.getValue();
+ }
+
+ captor = ArgumentCaptor.forClass(ContentObserver.class);
+ verify(mMockResolver, atMost(1)).registerContentObserver(
+ eq(Settings.System.getUriFor(Settings.System.USER_ROTATION)),
+ anyBoolean(),
+ captor.capture(),
+ anyInt());
+ if (!captor.getAllValues().isEmpty()) {
+ mUserRotationObserver = captor.getValue();
+ }
+ }
+
+ private Sensor createSensor(int type) throws Exception {
+ Constructor<Sensor> constr = Sensor.class.getDeclaredConstructor();
+ constr.setAccessible(true);
+ Sensor sensor = constr.newInstance();
+
+ setSensorType(sensor, type);
+ setSensorField(sensor, "mName", "Mock " + sensor.getStringType() + "/" + type);
+ setSensorField(sensor, "mVendor", "Mock Vendor");
+ setSensorField(sensor, "mVersion", 1);
+ setSensorField(sensor, "mHandle", -1);
+ setSensorField(sensor, "mMaxRange", 10);
+ setSensorField(sensor, "mResolution", 1);
+ setSensorField(sensor, "mPower", 1);
+ setSensorField(sensor, "mMinDelay", 1000);
+ setSensorField(sensor, "mMaxDelay", 1000000000);
+ setSensorField(sensor, "mFlags", 0);
+ setSensorField(sensor, "mId", -1);
+
+ return sensor;
+ }
+
+ private void setSensorType(Sensor sensor, int type) throws Exception {
+ Method setter = Sensor.class.getDeclaredMethod("setType", Integer.TYPE);
+ setter.setAccessible(true);
+ setter.invoke(sensor, type);
+ }
+
+ private void setSensorField(Sensor sensor, String fieldName, Object value)
+ throws Exception {
+ Field field = Sensor.class.getDeclaredField(fieldName);
+ field.setAccessible(true);
+ field.set(sensor, value);
+ }
+
+ private int convertRotationToDegrees(@Surface.Rotation int rotation) {
+ switch (rotation) {
+ case Surface.ROTATION_0:
+ return 0;
+ case Surface.ROTATION_90:
+ return 90;
+ case Surface.ROTATION_180:
+ return 180;
+ case Surface.ROTATION_270:
+ return 270;
+ default:
+ return -1;
+ }
+ }
+
+ private void build() throws Exception {
+ mMockContext = mock(Context.class);
+
+ mMockDisplayContent = mock(WindowTestUtils.TestDisplayContent.class);
+ mMockDisplayContent.isDefaultDisplay = mIsDefaultDisplay;
+
+ mMockDisplayPolicy = mock(DisplayPolicy.class);
+
+ mMockRes = mock(Resources.class);
+ when(mMockContext.getResources()).thenReturn((mMockRes));
+ when(mMockRes.getBoolean(com.android.internal.R.bool.config_supportAutoRotation))
+ .thenReturn(mSupportAutoRotation);
+ when(mMockRes.getInteger(com.android.internal.R.integer.config_lidOpenRotation))
+ .thenReturn(convertRotationToDegrees(mLidOpenRotation));
+ when(mMockRes.getInteger(com.android.internal.R.integer.config_carDockRotation))
+ .thenReturn(convertRotationToDegrees(mCarDockRotation));
+ when(mMockRes.getInteger(com.android.internal.R.integer.config_deskDockRotation))
+ .thenReturn(convertRotationToDegrees(mDeskDockRotation));
+ when(mMockRes.getInteger(com.android.internal.R.integer.config_undockedHdmiRotation))
+ .thenReturn(convertRotationToDegrees(mUndockedHdmiRotation));
+
+ mMockSensorManager = mock(SensorManager.class);
+ when(mMockContext.getSystemService(Context.SENSOR_SERVICE))
+ .thenReturn(mMockSensorManager);
+ mFakeSensor = createSensor(Sensor.TYPE_DEVICE_ORIENTATION);
+ when(mMockSensorManager.getSensorList(Sensor.TYPE_DEVICE_ORIENTATION)).thenReturn(
+ Collections.singletonList(mFakeSensor));
+
+ mMockResolver = mock(ContentResolver.class);
+ when(mMockContext.getContentResolver()).thenReturn(mMockResolver);
+ mFakeSettingsProvider = new FakeSettingsProvider();
+ when(mMockResolver.acquireProvider(Settings.AUTHORITY))
+ .thenReturn(mFakeSettingsProvider.getIContentProvider());
+
+ mMockDisplayWindowSettings = mock(DisplayWindowSettings.class);
+ mTarget = new DisplayRotation(mMockWm, mMockDisplayContent, mMockDisplayPolicy,
+ mMockDisplayWindowSettings, mMockContext, new Object());
+
+ captureObservers();
+ }
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
index 2c3c66b..f3a125b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
@@ -272,6 +272,51 @@
}
@Test
+ public void testClearsRecordInMemory() {
+ mTarget.saveTask(mTestTask);
+
+ mTarget.removeRecordForPackage(TEST_COMPONENT.getPackageName());
+
+ mTarget.getLaunchParams(mTestTask, null, mResult);
+
+ assertTrue("Result should be empty.", mResult.isEmpty());
+ }
+
+ @Test
+ public void testClearsWriteQueueItem() {
+ mTarget.saveTask(mTestTask);
+
+ mTarget.removeRecordForPackage(TEST_COMPONENT.getPackageName());
+
+ final LaunchParamsPersister target = new LaunchParamsPersister(mPersisterQueue, mSupervisor,
+ mUserFolderGetter);
+ target.onSystemReady();
+ target.onUnlockUser(TEST_USER_ID);
+
+ target.getLaunchParams(mTestTask, null, mResult);
+
+ assertTrue("Result should be empty.", mResult.isEmpty());
+ }
+
+ @Test
+ public void testClearsFile() {
+ mTarget.saveTask(mTestTask);
+ mPersisterQueue.flush();
+
+ mTarget.removeRecordForPackage(TEST_COMPONENT.getPackageName());
+
+ final LaunchParamsPersister target = new LaunchParamsPersister(mPersisterQueue, mSupervisor,
+ mUserFolderGetter);
+ target.onSystemReady();
+ target.onUnlockUser(TEST_USER_ID);
+
+ target.getLaunchParams(mTestTask, null, mResult);
+
+ assertTrue("Result should be empty.", mResult.isEmpty());
+ }
+
+
+ @Test
public void testClearsRecordInMemoryOnPackageUninstalled() {
mTarget.saveTask(mTestTask);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
index e56edab..3c87721 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
@@ -65,7 +65,7 @@
final DisplayRotation displayRotation = new DisplayRotation(
mock(WindowManagerService.class), displayContent, displayPolicy,
- context, new Object());
+ mock(DisplayWindowSettings.class), context, new Object());
displayRotation.mPortraitRotation = Surface.ROTATION_0;
displayRotation.mLandscapeRotation = Surface.ROTATION_90;
displayRotation.mUpsideDownRotation = Surface.ROTATION_180;
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 34a8c96..45cfe1e 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -569,14 +569,6 @@
public static final String IS_OPPORTUNISTIC = "is_opportunistic";
/**
- * TelephonyProvider column name for subId of parent subscription of an opportunistic
- * subscription.
- * if the parent sub id is valid, then is_opportunistic should always to true.
- * @hide
- */
- public static final String PARENT_SUB_ID = "parent_sub_id";
-
- /**
* TelephonyProvider column name for group ID. Subscriptions with same group ID
* are considered bundled together, and should behave as a single subscription at
* certain scenarios.
diff --git a/tests/Internal/src/android/service/wallpaper/WallpaperServiceTest.java b/tests/Internal/src/android/service/wallpaper/WallpaperServiceTest.java
index 8d8fc84..b9e282e 100644
--- a/tests/Internal/src/android/service/wallpaper/WallpaperServiceTest.java
+++ b/tests/Internal/src/android/service/wallpaper/WallpaperServiceTest.java
@@ -38,7 +38,7 @@
public Engine onCreateEngine() {
return new Engine() {
@Override
- public void onAmbientModeChanged(boolean inAmbientMode, boolean animated) {
+ public void onAmbientModeChanged(boolean inAmbientMode, long duration) {
ambientModeChangedCount[0]++;
}
};
@@ -47,12 +47,12 @@
WallpaperService.Engine engine = service.onCreateEngine();
engine.setCreated(true);
- engine.doAmbientModeChanged(false, false);
+ engine.doAmbientModeChanged(false, 0);
assertFalse("ambient mode should be false", engine.isInAmbientMode());
assertEquals("onAmbientModeChanged should have been called",
ambientModeChangedCount[0], 1);
- engine.doAmbientModeChanged(true, false);
+ engine.doAmbientModeChanged(true, 0);
assertTrue("ambient mode should be false", engine.isInAmbientMode());
assertEquals("onAmbientModeChanged should have been called",
ambientModeChangedCount[0], 2);
diff --git a/tools/powermodel/Android.bp b/tools/powermodel/Android.bp
new file mode 100644
index 0000000..f597aab
--- /dev/null
+++ b/tools/powermodel/Android.bp
@@ -0,0 +1,26 @@
+
+java_library_host {
+ name: "powermodel",
+ srcs: [
+ "src/**/*.java",
+ ],
+ static_libs: [
+ "guava",
+ ],
+}
+
+java_test_host {
+ name: "powermodel-test",
+
+ test_suites: ["general-tests"],
+
+ srcs: ["test/**/*.java"],
+ java_resource_dirs: ["test-resource"],
+
+ static_libs: [
+ "powermodel",
+ "junit",
+ "mockito",
+ ],
+}
+
diff --git a/tools/powermodel/TEST_MAPPING b/tools/powermodel/TEST_MAPPING
new file mode 100644
index 0000000..c8db339
--- /dev/null
+++ b/tools/powermodel/TEST_MAPPING
@@ -0,0 +1,8 @@
+{
+ "presubmit": [
+ {
+ "name": "powermodel-test"
+ }
+ ]
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/ActivityReport.java b/tools/powermodel/src/com/android/powermodel/ActivityReport.java
new file mode 100644
index 0000000..4a8f633
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/ActivityReport.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel;
+
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * ActivityReport contains the summary of the activity that consumes power
+ * as reported by batterystats or statsd.
+ */
+public class ActivityReport {
+ private AppList<AppActivity> mApps;
+
+ public ImmutableList<AppActivity> getAllApps() {
+ return mApps.getAllApps();
+ }
+
+ public ImmutableList<AppActivity> getRegularApps() {
+ return mApps.getRegularApps();
+ }
+
+ public List<AppActivity> findApp(String pkg) {
+ return mApps.findApp(pkg);
+ }
+
+ public AppActivity findApp(SpecialApp specialApp) {
+ return mApps.findApp(specialApp);
+ }
+
+ /**
+ * Find a component in the GLOBAL app.
+ * <p>
+ * Returns null if either the global app doesn't exist (bad data?) or the component
+ * doesn't exist in the global app.
+ */
+ public ComponentActivity findGlobalComponent(Component component) {
+ final AppActivity global = mApps.findApp(SpecialApp.GLOBAL);
+ if (global == null) {
+ return null;
+ }
+ return global.getComponentActivity(component);
+ }
+
+ public static class Builder {
+ private AppList.Builder<AppActivity,AppActivity.Builder> mApps = new AppList.Builder();
+
+ public Builder() {
+ }
+
+ public ActivityReport build() {
+ final ActivityReport result = new ActivityReport();
+ result.mApps = mApps.build();
+ return result;
+ }
+
+ public void addActivity(Component component, Collection<ComponentActivity> activities) {
+ for (final ComponentActivity activity: activities) {
+ addActivity(component, activity);
+ }
+ }
+
+ public void addActivity(Component component, ComponentActivity activity) {
+ AppActivity.Builder app = mApps.get(activity.attribution);
+ if (app == null) {
+ app = new AppActivity.Builder();
+ app.setAttribution(activity.attribution);
+ mApps.put(activity.attribution, app);
+ }
+ app.addComponentActivity(component, activity);
+ }
+ }
+}
diff --git a/tools/powermodel/src/com/android/powermodel/AppActivity.java b/tools/powermodel/src/com/android/powermodel/AppActivity.java
new file mode 100644
index 0000000..b87426c
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/AppActivity.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel;
+
+import java.util.HashMap;
+
+import com.google.common.collect.ImmutableCollection;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+
+public class AppActivity extends AppInfo {
+
+ private ImmutableMap<Component, ComponentActivity> mComponents;
+ // TODO: power rails
+ // private ImmutableMap<Component, PowerRailActivity> mRails;
+
+ private AppActivity() {
+ }
+
+ /**
+ * Returns the {@link ComponentActivity} for the {@link Component} provided,
+ * or null if this AppActivity does not have that component.
+ * @more
+ * If there is no ComponentActivity for a particular Component, then
+ * there was no usage associated with that app for the app in question.
+ */
+ public ComponentActivity getComponentActivity(Component component) {
+ return mComponents.get(component);
+ }
+
+ public ImmutableSet<Component> getComponents() {
+ return mComponents.keySet();
+ }
+
+ public ImmutableMap<Component,ComponentActivity> getComponentActivities() {
+ return mComponents;
+ }
+
+ // TODO: power rails
+ // public ComponentActivity getPowerRail(Component component) {
+ // return mComponents.get(component);
+ // }
+ //
+ // public Set<Component> getPowerRails() {
+ // return mComponents.keySet();
+ // }
+
+ public static class Builder extends AppInfo.Builder<AppActivity> {
+ private HashMap<Component, ComponentActivity> mComponents = new HashMap();
+ // TODO power rails.
+
+ public Builder() {
+ }
+
+ public AppActivity build() {
+ final AppActivity result = new AppActivity();
+ init(result);
+ result.mComponents = ImmutableMap.copyOf(mComponents);
+ return result;
+ }
+
+ public void addComponentActivity(Component component, ComponentActivity activity) {
+ mComponents.put(component, activity);
+ }
+ }
+}
diff --git a/tools/powermodel/src/com/android/powermodel/AppInfo.java b/tools/powermodel/src/com/android/powermodel/AppInfo.java
new file mode 100644
index 0000000..208339e
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/AppInfo.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel;
+
+class AppInfo {
+ private AttributionKey mAttribution;
+
+ protected AppInfo() {
+ }
+
+ public boolean hasPackage(String pkg) {
+ return mAttribution.hasPackage(pkg);
+ }
+
+ public AttributionKey getAttribution() {
+ return mAttribution;
+ }
+
+ abstract static class Builder<APP extends AppInfo> {
+ private AttributionKey mAttribution;
+
+ public Builder() {
+ }
+
+ public abstract APP build();
+
+ protected void init(AppInfo app) {
+ if (mAttribution == null) {
+ throw new RuntimeException("setAttribution(AttributionKey attribution) not called");
+ }
+ app.mAttribution = mAttribution;
+ }
+
+ public void setAttribution(AttributionKey attribution) {
+ mAttribution = attribution;
+ }
+
+ public AttributionKey getAttribution() {
+ return mAttribution;
+ }
+ }
+}
diff --git a/tools/powermodel/src/com/android/powermodel/AppList.java b/tools/powermodel/src/com/android/powermodel/AppList.java
new file mode 100644
index 0000000..19572fc
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/AppList.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+class AppList<APP extends AppInfo> {
+ private ImmutableList<APP> mAllApps;
+ private ImmutableList<APP> mRegularApps;
+ private ImmutableMap<SpecialApp,APP> mSpecialApps;
+
+ private AppList() {
+ }
+
+ public ImmutableList<APP> getAllApps() {
+ return mAllApps;
+ }
+
+ public ImmutableList<APP> getRegularApps() {
+ return mRegularApps;
+ }
+
+ public List<APP> findApp(String pkg) {
+ List<APP> result = new ArrayList();
+ for (APP app: mRegularApps) {
+ if (app.hasPackage(pkg)) {
+ result.add(app);
+ }
+ }
+ return result;
+ }
+
+ public APP findApp(SpecialApp specialApp) {
+ return mSpecialApps.get(specialApp);
+ }
+
+ public static class Builder<APP extends AppInfo, BUILDER extends AppInfo.Builder<APP>> {
+ private final HashMap<AttributionKey,BUILDER> mApps = new HashMap();
+
+ public Builder() {
+ }
+
+ public AppList<APP> build() {
+ final AppList<APP> result = new AppList();
+ final ArrayList<APP> allApps = new ArrayList();
+ final ArrayList<APP> regularApps = new ArrayList();
+ final HashMap<SpecialApp,APP> specialApps = new HashMap();
+ for (AppInfo.Builder<APP> app: mApps.values()) {
+ final AttributionKey attribution = app.getAttribution();
+ final APP appActivity = app.build();
+ allApps.add(appActivity);
+ if (attribution.isSpecialApp()) {
+ specialApps.put(attribution.getSpecialApp(), appActivity);
+ } else {
+ regularApps.add(appActivity);
+ }
+ }
+ result.mAllApps = ImmutableList.copyOf(allApps);
+ result.mRegularApps = ImmutableList.copyOf(regularApps);
+ result.mSpecialApps = ImmutableMap.copyOf(specialApps);
+ return result;
+ }
+
+ public BUILDER get(AttributionKey attribution) {
+ return mApps.get(attribution);
+ }
+
+ public BUILDER put(AttributionKey attribution, BUILDER app) {
+ return mApps.put(attribution, app);
+ }
+ }
+}
diff --git a/tools/powermodel/src/com/android/powermodel/AppPower.java b/tools/powermodel/src/com/android/powermodel/AppPower.java
new file mode 100644
index 0000000..283982b
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/AppPower.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel;
+
+import java.util.HashMap;
+import java.util.Set;
+
+import com.google.common.collect.ImmutableMap;
+
+public class AppPower extends AppInfo {
+ private ImmutableMap<Component, ComponentPower> mComponents;
+
+ private double mAppPowerMah;
+
+
+ private AppPower() {
+ }
+
+ /**
+ * Returns the {@link ComponentPower} for the {@link Component} provided,
+ * or null if this AppPower does not have that component.
+ * @more
+ * If the component was in the power profile for this device, there
+ * will be a component for it, even if there was no power used
+ * by that component. In that case, the
+ * {@link ComponentPower.getUsage() ComponentPower.getUsage()}
+ * method will return 0.
+ */
+ public ComponentPower getComponentPower(Component component) {
+ return mComponents.get(component);
+ }
+
+ public Set<Component> getComponents() {
+ return mComponents.keySet();
+ }
+
+ /**
+ * Return the total power used by this app.
+ */
+ public double getAppPowerMah() {
+ return mAppPowerMah;
+ }
+
+ /**
+ * Builder class for {@link AppPower}
+ */
+ public static class Builder extends AppInfo.Builder<AppPower> {
+ private HashMap<Component, ComponentPower> mComponents = new HashMap();
+
+ public Builder() {
+ }
+
+ public AppPower build() {
+ final AppPower result = new AppPower();
+ init(result);
+ result.mComponents = ImmutableMap.copyOf(mComponents);
+
+ // Add up the components
+ double appPowerMah = 0;
+ for (final ComponentPower componentPower: mComponents.values()) {
+ appPowerMah += componentPower.powerMah;
+ }
+ result.mAppPowerMah = appPowerMah;
+
+ return result;
+ }
+
+ public void addComponentPower(Component component, ComponentPower componentPower) {
+ mComponents.put(component, componentPower);
+ }
+ }
+}
diff --git a/tools/powermodel/src/com/android/powermodel/AttributionKey.java b/tools/powermodel/src/com/android/powermodel/AttributionKey.java
new file mode 100644
index 0000000..f19e0b7
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/AttributionKey.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel;
+
+import java.util.Set;
+import java.util.HashSet;
+
+import com.google.common.collect.ImmutableSet;
+
+public class AttributionKey {
+ private final int mUid;
+ private final ImmutableSet<String> mPackages;
+ private final SpecialApp mSpecialApp;
+
+ public AttributionKey(SpecialApp specialApp) {
+ mUid = -1;
+ mPackages = ImmutableSet.of();
+ mSpecialApp = specialApp;
+ }
+
+ public AttributionKey(int uid, Set<String> packages) {
+ mUid = uid;
+ mPackages = ImmutableSet.copyOf(packages);
+ mSpecialApp = null;
+ }
+
+ public ImmutableSet<String> getPackages() {
+ return mPackages;
+ }
+
+ public boolean hasPackage(String pkg) {
+ return mPackages.contains(pkg);
+ }
+
+ public SpecialApp getSpecialApp() {
+ return mSpecialApp;
+ }
+
+ public boolean isSpecialApp() {
+ return mSpecialApp != null;
+ }
+
+ /**
+ * Returns the uid for this attribution, or -1 if there isn't one
+ * (e.g. if it is a special app).
+ */
+ public int getUid() {
+ return mUid;
+ }
+ @Override
+ public int hashCode() {
+ int hash = 7;
+ hash = (31 * hash) + (mUid);
+ hash = (31 * hash) + (mPackages == null ? 0 : mPackages.hashCode());
+ hash = (31 * hash) + (mSpecialApp == null ? 0 : mSpecialApp.hashCode());
+ return hash;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null) {
+ return false;
+ }
+ if (this.getClass() != o.getClass()) {
+ return false;
+ }
+ final AttributionKey that = (AttributionKey)o;
+ return (this.mUid == that.mUid)
+ && this.mPackages != null && this.mPackages.equals(that.mPackages)
+ && this.mSpecialApp != null && this.mSpecialApp.equals(that.mSpecialApp);
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder str = new StringBuilder("AttributionKey(");
+ if (mUid >= 0) {
+ str.append(" uid=");
+ str.append(mUid);
+ }
+ if (mPackages.size() > 0) {
+ str.append(" packages=[");
+ for (String pkg: mPackages) {
+ str.append(' ');
+ str.append(pkg);
+ }
+ str.append(" ]");
+ }
+ if (mSpecialApp != null) {
+ str.append(" specialApp=");
+ str.append(mSpecialApp.name());
+ }
+ str.append(" )");
+ return str.toString();
+ }
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/BatteryStatsReader.java b/tools/powermodel/src/com/android/powermodel/BatteryStatsReader.java
new file mode 100644
index 0000000..595c661
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/BatteryStatsReader.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel;
+
+import java.io.InputStream;
+import java.io.IOException;
+import com.android.powermodel.component.ModemBatteryStatsReader;
+
+public class BatteryStatsReader {
+ /**
+ * Construct a reader.
+ */
+ public BatteryStatsReader() {
+ }
+
+ /**
+ * Parse a powermodel.xml file and return a PowerProfile object.
+ *
+ * @param stream An InputStream containing the batterystats output.
+ *
+ * @throws ParseException Thrown when the xml file can not be parsed.
+ * @throws IOException When there is a problem reading the stream.
+ */
+ public static ActivityReport parse(InputStream stream) throws ParseException, IOException {
+ final Parser parser = new Parser(stream);
+ return parser.parse();
+ }
+
+ /**
+ * Implements the reading and power model logic.
+ */
+ private static class Parser {
+ final InputStream mStream;
+ final ActivityReport mResult;
+ RawBatteryStats mBs;
+
+ /**
+ * Constructor to capture the parameters to read.
+ */
+ Parser(InputStream stream) {
+ mStream = stream;
+ mResult = new ActivityReport();
+ }
+
+ /**
+ * Read the stream, parse it, and apply the power model.
+ * Do not call this more than once.
+ */
+ ActivityReport parse() throws ParseException, IOException {
+ mBs = RawBatteryStats.parse(mStream);
+
+ final ActivityReport.Builder report = new ActivityReport.Builder();
+
+ report.addActivity(Component.MODEM, ModemBatteryStatsReader.createActivities(mBs));
+
+ return report.build();
+ }
+ }
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/Component.java b/tools/powermodel/src/com/android/powermodel/Component.java
new file mode 100644
index 0000000..baae6d7
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/Component.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel;
+
+/**
+ * The hardware components that use power on a device.
+ */
+public enum Component {
+ CPU,
+ SCREEN,
+ MODEM,
+ WIFI,
+ BLUETOOTH,
+ VIDEO,
+ AUDIO,
+ FLASHLIGHT,
+ CAMERA,
+ GPS,
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/ComponentActivity.java b/tools/powermodel/src/com/android/powermodel/ComponentActivity.java
new file mode 100644
index 0000000..c1e2662
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/ComponentActivity.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel;
+
+
+/**
+ * Encapsulates the work done by an app (including synthetic apps) that costs power.
+ */
+public class ComponentActivity {
+ public AttributionKey attribution;
+
+ protected ComponentActivity(AttributionKey attribution) {
+ this.attribution = attribution;
+ }
+
+ // TODO: Can we refactor what goes into the activities so this function
+ // doesn't need the global state?
+ /**
+ * Apply the power profile for this component. Subclasses should implement this
+ * to do the per-component calculatinos. The default implementation returns null.
+ * If this method returns null, then there will be no power associated for this
+ * component, which, for example is true with some of the GLOBAL activities.
+ */
+ public ComponentPower applyProfile(ActivityReport activityReport, PowerProfile profile) {
+ return null;
+ }
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/ComponentPower.java b/tools/powermodel/src/com/android/powermodel/ComponentPower.java
new file mode 100644
index 0000000..b22ff87
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/ComponentPower.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel;
+
+/**
+ * The hardware component that uses power on a device.
+ * <p>
+ * This base class contains the total power used by each Component in an app.
+ * Subclasses may add more detail, which is a drill-down, but is not to be
+ * <i>added</i> to {@link #powerMah}.
+ */
+public abstract class ComponentPower<ACTIVITY extends ComponentActivity> {
+ /**
+ * The app associated with this ComponentPower.
+ */
+ public AttributionKey attribution;
+
+ /**
+ * The app activity that resulted in the power usage for this component.
+ */
+ public ACTIVITY activity;
+
+ /**
+ * The total power used by this component in this app.
+ */
+ public double powerMah;
+}
diff --git a/tools/powermodel/src/com/android/powermodel/ComponentProfile.java b/tools/powermodel/src/com/android/powermodel/ComponentProfile.java
new file mode 100644
index 0000000..e76e5fb
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/ComponentProfile.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel;
+
+public class ComponentProfile {
+}
diff --git a/tools/powermodel/src/com/android/powermodel/CsvParser.java b/tools/powermodel/src/com/android/powermodel/CsvParser.java
new file mode 100644
index 0000000..78cd261
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/CsvParser.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+
+/**
+ * Parses CSV.
+ * <p>
+ * Call parse() with an InputStream.
+ * <p>
+ * CsvLineProcessor.onLine() will be called for each line in the source document.
+ * <p>
+ * To simplify parsing and to protect against using too much memory for bad
+ * data, the maximum field length is {@link #MAX_FIELD_SIZE}.
+ */
+class CsvParser {
+ /**
+ * The maximum size of a single field in bytes.
+ */
+ public static final int MAX_FIELD_SIZE = (8*1024)-1;
+
+ /**
+ * Callback interface for each line of CSV as it is parsed.
+ */
+ interface LineProcessor {
+ /**
+ * A line of CSV was parsed.
+ *
+ * @param lineNumber the line number in the file, starting at 1
+ * @param fields the comma separated fields for the line
+ */
+ void onLine(int lineNumber, ArrayList<String> fields) throws ParseException;
+ }
+
+ /**
+ * Parse the CSV text in input, calling onto processor for each row.
+ */
+ public static void parse(InputStream input, LineProcessor processor)
+ throws IOException, ParseException {
+ final Charset utf8 = StandardCharsets.UTF_8;
+ final byte[] buf = new byte[MAX_FIELD_SIZE+1];
+ int lineNumber = 1;
+ int readPos = 0;
+ int prev = 0;
+ ArrayList<String> fields = new ArrayList<String>();
+ boolean finalBuffer = false;
+ boolean escaping = false;
+ boolean sawQuote = false;
+
+ while (!finalBuffer) {
+ int amt = input.read(buf, readPos, buf.length-readPos);
+ if (amt < 0) {
+ // No more data. Process whatever's left from before.
+ amt = readPos;
+ finalBuffer = true;
+ } else {
+ // Process whatever's left from before, plus the new data.
+ amt += readPos;
+ finalBuffer = false;
+ }
+
+ // Process as much of this buffer as we can.
+ int fieldStart = 0;
+ int index = readPos;
+ int escapeIndex = escaping ? readPos : -1;
+ while (index < amt) {
+ byte c = buf[index];
+ if (c == '\r' || c == '\n') {
+ if (escaping) {
+ // TODO: Quotes do not escape newlines in our CSV dialect,
+ // but we actually see some data where it should.
+ fields.add(new String(buf, fieldStart, escapeIndex-fieldStart));
+ escapeIndex = -1;
+ escaping = false;
+ sawQuote = false;
+ } else {
+ fields.add(new String(buf, fieldStart, index-fieldStart));
+ }
+ // Don't report blank lines
+ if (fields.size() > 1 || (fields.size() == 1 && fields.get(0).length() > 0)) {
+ processor.onLine(lineNumber, fields);
+ }
+ fields = new ArrayList<String>();
+ if (!(c == '\n' && prev == '\r')) {
+ // Don't double increment for dos line endings.
+ lineNumber++;
+ }
+ fieldStart = index = index + 1;
+ } else {
+ if (escaping) {
+ // Field started with a " so quotes are escaped with " and commas
+ // don't matter except when following a single quote.
+ if (c == '"') {
+ if (sawQuote) {
+ buf[escapeIndex] = buf[index];
+ escapeIndex++;
+ sawQuote = false;
+ } else {
+ sawQuote = true;
+ }
+ index++;
+ } else if (sawQuote && c == ',') {
+ fields.add(new String(buf, fieldStart, escapeIndex-fieldStart));
+ fieldStart = index = index + 1;
+ escapeIndex = -1;
+ escaping = false;
+ sawQuote = false;
+ } else {
+ buf[escapeIndex] = buf[index];
+ escapeIndex++;
+ index++;
+ sawQuote = false;
+ }
+ } else {
+ if (c == ',') {
+ fields.add(new String(buf, fieldStart, index-fieldStart));
+ fieldStart = index + 1;
+ } else if (c == '"' && fieldStart == index) {
+ // First character is a "
+ escaping = true;
+ fieldStart = escapeIndex = index + 1;
+ }
+ index++;
+ }
+ }
+ prev = c;
+ }
+
+ // A single field is greater than buf.length, so fail.
+ if (fieldStart == 0 && index == buf.length) {
+ throw new ParseException(lineNumber, "Line is too long: "
+ + new String(buf, 0, 20, utf8) + "...");
+ }
+
+ // Move whatever we didn't process to the beginning of the buffer
+ // and try again.
+ if (fieldStart != amt) {
+ readPos = (escaping ? escapeIndex : index) - fieldStart;
+ System.arraycopy(buf, fieldStart, buf, 0, readPos);
+ } else {
+ readPos = 0;
+ }
+
+ // Process whatever's left over
+ if (finalBuffer) {
+ fields.add(new String(buf, 0, readPos));
+ // If there is any content, return the last line.
+ if (fields.size() > 1 || (fields.size() == 1 && fields.get(0).length() > 0)) {
+ processor.onLine(lineNumber, fields);
+ }
+ }
+ }
+ }
+}
diff --git a/tools/powermodel/src/com/android/powermodel/ParseException.java b/tools/powermodel/src/com/android/powermodel/ParseException.java
new file mode 100644
index 0000000..e1f232b
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/ParseException.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel;
+
+public class ParseException extends Exception {
+ public final int line;
+
+ public ParseException(int line, String message, Throwable th) {
+ super(message, th);
+ this.line = line;
+ }
+
+ public ParseException(int line, String message) {
+ this(line, message, null);
+ }
+
+ public ParseException(String message) {
+ this(0, message, null);
+ }
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/PowerProfile.java b/tools/powermodel/src/com/android/powermodel/PowerProfile.java
new file mode 100644
index 0000000..373a9c9
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/PowerProfile.java
@@ -0,0 +1,527 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+import org.xml.sax.Attributes;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import com.android.powermodel.component.AudioProfile;
+import com.android.powermodel.component.BluetoothProfile;
+import com.android.powermodel.component.CameraProfile;
+import com.android.powermodel.component.CpuProfile;
+import com.android.powermodel.component.FlashlightProfile;
+import com.android.powermodel.component.GpsProfile;
+import com.android.powermodel.component.ModemProfile;
+import com.android.powermodel.component.ScreenProfile;
+import com.android.powermodel.component.VideoProfile;
+import com.android.powermodel.component.WifiProfile;
+import com.android.powermodel.util.Conversion;
+
+public class PowerProfile {
+
+ // Remaining fields from the android code for which the actual usage is unclear.
+ // battery.capacity
+ // bluetooth.controller.voltage
+ // modem.controller.voltage
+ // gps.voltage
+ // wifi.controller.voltage
+ // radio.on
+ // radio.scanning
+ // radio.active
+ // memory.bandwidths
+ // wifi.batchedscan
+ // wifi.scan
+ // wifi.on
+ // wifi.active
+ // wifi.controller.tx_levels
+
+ private static Pattern RE_CLUSTER_POWER = Pattern.compile("cpu.cluster_power.cluster([0-9]*)");
+ private static Pattern RE_CORE_SPEEDS = Pattern.compile("cpu.core_speeds.cluster([0-9]*)");
+ private static Pattern RE_CORE_POWER = Pattern.compile("cpu.core_power.cluster([0-9]*)");
+
+ private HashMap<Component, ComponentProfile> mComponents = new HashMap();
+
+ /**
+ * Which element we are currently parsing.
+ */
+ enum ElementState {
+ BEGIN,
+ TOP,
+ ITEM,
+ ARRAY,
+ VALUE
+ }
+
+ /**
+ * Implements the reading and power model logic.
+ */
+ private static class Parser {
+ private final InputStream mStream;
+ private final PowerProfile mResult;
+
+ // Builders for the ComponentProfiles.
+ private final AudioProfile mAudio = new AudioProfile();
+ private final BluetoothProfile mBluetooth = new BluetoothProfile();
+ private final CameraProfile mCamera = new CameraProfile();
+ private final CpuProfile.Builder mCpuBuilder = new CpuProfile.Builder();
+ private final FlashlightProfile mFlashlight = new FlashlightProfile();
+ private final GpsProfile.Builder mGpsBuilder = new GpsProfile.Builder();
+ private final ModemProfile.Builder mModemBuilder = new ModemProfile.Builder();
+ private final ScreenProfile mScreen = new ScreenProfile();
+ private final VideoProfile mVideo = new VideoProfile();
+ private final WifiProfile mWifi = new WifiProfile();
+
+ /**
+ * Constructor to capture the parameters to read.
+ */
+ Parser(InputStream stream) {
+ mStream = stream;
+ mResult = new PowerProfile();
+ }
+
+ /**
+ * Read the stream, parse it, and apply the power model.
+ * Do not call this more than once.
+ */
+ PowerProfile parse() throws ParseException {
+ final SAXParserFactory factory = SAXParserFactory.newInstance();
+ AndroidResourceHandler handler = null;
+ try {
+ final SAXParser saxParser = factory.newSAXParser();
+
+ handler = new AndroidResourceHandler() {
+ @Override
+ public void onItem(Locator locator, String name, float value)
+ throws SAXParseException {
+ Parser.this.onItem(locator, name, value);
+ }
+
+ @Override
+ public void onArray(Locator locator, String name, float[] value)
+ throws SAXParseException {
+ Parser.this.onArray(locator, name, value);
+ }
+ };
+
+ saxParser.parse(mStream, handler);
+ } catch (ParserConfigurationException ex) {
+ // Coding error, not runtime error.
+ throw new RuntimeException(ex);
+ } catch (SAXParseException ex) {
+ throw new ParseException(ex.getLineNumber(), ex.getMessage(), ex);
+ } catch (SAXException | IOException ex) {
+ // Make a guess about the line number.
+ throw new ParseException(handler.getLineNumber(), ex.getMessage(), ex);
+ }
+
+ // TODO: This doesn't cover the multiple algorithms. Some refactoring will
+ // be necessary.
+ mResult.mComponents.put(Component.AUDIO, mAudio);
+ mResult.mComponents.put(Component.BLUETOOTH, mBluetooth);
+ mResult.mComponents.put(Component.CAMERA, mCamera);
+ mResult.mComponents.put(Component.CPU, mCpuBuilder.build());
+ mResult.mComponents.put(Component.FLASHLIGHT, mFlashlight);
+ mResult.mComponents.put(Component.GPS, mGpsBuilder.build());
+ mResult.mComponents.put(Component.MODEM, mModemBuilder.build());
+ mResult.mComponents.put(Component.SCREEN, mScreen);
+ mResult.mComponents.put(Component.VIDEO, mVideo);
+ mResult.mComponents.put(Component.WIFI, mWifi);
+
+ return mResult;
+ }
+
+ /**
+ * Handles an item tag in the power_profile.xml.
+ */
+ public void onItem(Locator locator, String name, float value) throws SAXParseException {
+ Integer index;
+ try {
+ if ("ambient.on".equals(name)) {
+ mScreen.ambientMa = value;
+ } else if ("audio".equals(name)) {
+ mAudio.onMa = value;
+ } else if ("bluetooth.controller.idle".equals(name)) {
+ mBluetooth.idleMa = value;
+ } else if ("bluetooth.controller.rx".equals(name)) {
+ mBluetooth.rxMa = value;
+ } else if ("bluetooth.controller.tx".equals(name)) {
+ mBluetooth.txMa = value;
+ } else if ("camera.avg".equals(name)) {
+ mCamera.onMa = value;
+ } else if ("camera.flashlight".equals(name)) {
+ mFlashlight.onMa = value;
+ } else if ("cpu.suspend".equals(name)) {
+ mCpuBuilder.setSuspendMa(value);
+ } else if ("cpu.idle".equals(name)) {
+ mCpuBuilder.setIdleMa(value);
+ } else if ("cpu.active".equals(name)) {
+ mCpuBuilder.setActiveMa(value);
+ } else if ((index = matchIndexedRegex(locator, RE_CLUSTER_POWER, name)) != null) {
+ mCpuBuilder.setClusterPower(index, value);
+ } else if ("gps.on".equals(name)) {
+ mGpsBuilder.setOnMa(value);
+ } else if ("modem.controller.sleep".equals(name)) {
+ mModemBuilder.setSleepMa(value);
+ } else if ("modem.controller.idle".equals(name)) {
+ mModemBuilder.setIdleMa(value);
+ } else if ("modem.controller.rx".equals(name)) {
+ mModemBuilder.setRxMa(value);
+ } else if ("radio.scanning".equals(name)) {
+ mModemBuilder.setScanningMa(value);
+ } else if ("screen.on".equals(name)) {
+ mScreen.onMa = value;
+ } else if ("screen.full".equals(name)) {
+ mScreen.fullMa = value;
+ } else if ("video".equals(name)) {
+ mVideo.onMa = value;
+ } else if ("wifi.controller.idle".equals(name)) {
+ mWifi.idleMa = value;
+ } else if ("wifi.controller.rx".equals(name)) {
+ mWifi.rxMa = value;
+ } else if ("wifi.controller.tx".equals(name)) {
+ mWifi.txMa = value;
+ } else {
+ // TODO: Uncomment this when we have all of the items parsed.
+ // throw new SAXParseException("Unhandled <item name=\"" + name + "\"> element",
+ // locator, ex);
+
+ }
+ } catch (ParseException ex) {
+ throw new SAXParseException(ex.getMessage(), locator, ex);
+ }
+ }
+
+ /**
+ * Handles an array tag in the power_profile.xml.
+ */
+ public void onArray(Locator locator, String name, float[] value) throws SAXParseException {
+ Integer index;
+ try {
+ if ("cpu.clusters.cores".equals(name)) {
+ mCpuBuilder.setCoreCount(Conversion.toIntArray(value));
+ } else if ((index = matchIndexedRegex(locator, RE_CORE_SPEEDS, name)) != null) {
+ mCpuBuilder.setCoreSpeeds(index, Conversion.toIntArray(value));
+ } else if ((index = matchIndexedRegex(locator, RE_CORE_POWER, name)) != null) {
+ mCpuBuilder.setCorePower(index, value);
+ } else if ("gps.signalqualitybased".equals(name)) {
+ mGpsBuilder.setSignalMa(value);
+ } else if ("modem.controller.tx".equals(name)) {
+ mModemBuilder.setTxMa(value);
+ } else {
+ // TODO: Uncomment this when we have all of the items parsed.
+ // throw new SAXParseException("Unhandled <item name=\"" + name + "\"> element",
+ // locator, ex);
+ }
+ } catch (ParseException ex) {
+ throw new SAXParseException(ex.getMessage(), locator, ex);
+ }
+ }
+ }
+
+ /**
+ * SAX XML handler that can parse the android resource files.
+ * In our case, all elements are floats.
+ */
+ abstract static class AndroidResourceHandler extends DefaultHandler {
+ /**
+ * The set of names already processed. Map of name to line number.
+ */
+ private HashMap<String,Integer> mAlreadySeen = new HashMap<String,Integer>();
+
+ /**
+ * Where in the document we are parsing.
+ */
+ private Locator mLocator;
+
+ /**
+ * Which element we are currently parsing.
+ */
+ private ElementState mState = ElementState.BEGIN;
+
+ /**
+ * Saved name from item and array elements.
+ */
+ private String mName;
+
+ /**
+ * The text that is currently being captured, or null if {@link #startCapturingText()}
+ * has not been called.
+ */
+ private StringBuilder mText;
+
+ /**
+ * The array values that have been parsed so for for this array. Null if we are
+ * not inside an array tag.
+ */
+ private ArrayList<Float> mArray;
+
+ /**
+ * Called when an item tag is encountered.
+ */
+ public abstract void onItem(Locator locator, String name, float value)
+ throws SAXParseException;
+
+ /**
+ * Called when an array is encountered.
+ */
+ public abstract void onArray(Locator locator, String name, float[] value)
+ throws SAXParseException;
+
+ /**
+ * If we have a Locator set, return the line number, otherwise return 0.
+ */
+ public int getLineNumber() {
+ return mLocator != null ? mLocator.getLineNumber() : 0;
+ }
+
+ /**
+ * Handle setting the parse location object.
+ */
+ public void setDocumentLocator(Locator locator) {
+ mLocator = locator;
+ }
+
+ /**
+ * Handle beginning of an element.
+ *
+ * @param ns Namespace uri
+ * @param ln Local name (inside namespace)
+ * @param element Tag name
+ */
+ @Override
+ public void startElement(String ns, String ln, String element,
+ Attributes attr) throws SAXException {
+ switch (mState) {
+ case BEGIN:
+ // Outer element, we don't care the tag name.
+ mState = ElementState.TOP;
+ return;
+ case TOP:
+ if ("item".equals(element)) {
+ mState = ElementState.ITEM;
+ saveNameAttribute(attr);
+ startCapturingText();
+ return;
+ } else if ("array".equals(element)) {
+ mState = ElementState.ARRAY;
+ mArray = new ArrayList<Float>();
+ saveNameAttribute(attr);
+ return;
+ }
+ break;
+ case ARRAY:
+ if ("value".equals(element)) {
+ mState = ElementState.VALUE;
+ startCapturingText();
+ return;
+ }
+ break;
+ }
+ throw new SAXParseException("unexpected element: '" + element + "'", mLocator);
+ }
+
+ /**
+ * Handle end of an element.
+ *
+ * @param ns Namespace uri
+ * @param ln Local name (inside namespace)
+ * @param element Tag name
+ */
+ @Override
+ public void endElement(String ns, String ln, String element) throws SAXException {
+ switch (mState) {
+ case ITEM: {
+ float value = parseFloat(finishCapturingText());
+ mState = ElementState.TOP;
+ onItem(mLocator, mName, value);
+ break;
+ }
+ case ARRAY: {
+ final int N = mArray.size();
+ float[] values = new float[N];
+ for (int i=0; i<N; i++) {
+ values[i] = mArray.get(i);
+ }
+ mArray = null;
+ mState = ElementState.TOP;
+ onArray(mLocator, mName, values);
+ break;
+ }
+ case VALUE: {
+ mArray.add(parseFloat(finishCapturingText()));
+ mState = ElementState.ARRAY;
+ break;
+ }
+ }
+ }
+
+ /**
+ * Interstitial text received.
+ *
+ * @throws SAXException if there shouldn't be non-whitespace text here
+ */
+ @Override
+ public void characters(char text[], int start, int length) throws SAXException {
+ if (mText == null && length > 0 && !isWhitespace(text, start, length)) {
+ throw new SAXParseException("unexpected text: '"
+ + firstLine(text, start, length).trim() + "'", mLocator);
+ }
+ if (mText != null) {
+ mText.append(text, start, length);
+ }
+ }
+
+ /**
+ * Begin collecting text from inside an element.
+ */
+ private void startCapturingText() {
+ if (mText != null) {
+ throw new RuntimeException("ASSERTION FAILED: Shouldn't be already capturing"
+ + " text. mState=" + mState.name()
+ + " line=" + mLocator.getLineNumber()
+ + " column=" + mLocator.getColumnNumber());
+ }
+ mText = new StringBuilder();
+ }
+
+ /**
+ * Stop capturing text from inside an element.
+ *
+ * @return the captured text
+ */
+ private String finishCapturingText() {
+ if (mText == null) {
+ throw new RuntimeException("ASSERTION FAILED: Should already be capturing"
+ + " text. mState=" + mState.name()
+ + " line=" + mLocator.getLineNumber()
+ + " column=" + mLocator.getColumnNumber());
+ }
+ final String result = mText.toString().trim();
+ mText = null;
+ return result;
+ }
+
+ /**
+ * Get the "name" attribute.
+ *
+ * @throws SAXParseException if the name attribute is not present or if
+ * the name has already been seen in the file.
+ */
+ private void saveNameAttribute(Attributes attr) throws SAXParseException {
+ final String name = attr.getValue("name");
+ if (name == null) {
+ throw new SAXParseException("expected 'name' attribute", mLocator);
+ }
+ Integer prev = mAlreadySeen.put(name, mLocator.getLineNumber());
+ if (prev != null) {
+ throw new SAXParseException("name '" + name + "' already seen on line: " + prev,
+ mLocator);
+ }
+ mName = name;
+ }
+
+ /**
+ * Gets the float value of the string.
+ *
+ * @throws SAXParseException if 'text' can't be parsed as a float.
+ */
+ private float parseFloat(String text) throws SAXParseException {
+ try {
+ return Float.parseFloat(text);
+ } catch (NumberFormatException ex) {
+ throw new SAXParseException("not a valid float value: '" + text + "'",
+ mLocator, ex);
+ }
+ }
+ }
+
+ /**
+ * Return whether the given substring is all whitespace.
+ */
+ private static boolean isWhitespace(char[] text, int start, int length) {
+ for (int i = start; i < (start + length); i++) {
+ if (!Character.isSpace(text[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Return the contents of text up to the first newline.
+ */
+ private static String firstLine(char[] text, int start, int length) {
+ // TODO: The line number will be wrong if we skip preceeding blank lines.
+ while (length > 0) {
+ if (Character.isSpace(text[start])) {
+ start++;
+ length--;
+ }
+ }
+ int newlen = 0;
+ for (; newlen < length; newlen++) {
+ final char c = text[newlen];
+ if (c == '\n' || c == '\r') {
+ break;
+ }
+ }
+ return new String(text, start, newlen);
+ }
+
+ /**
+ * If the pattern matches, return the first group of that as an Integer.
+ * If not return null.
+ */
+ private static Integer matchIndexedRegex(Locator locator, Pattern pattern, String text)
+ throws SAXParseException {
+ final Matcher m = pattern.matcher(text);
+ if (m.matches()) {
+ try {
+ return Integer.parseInt(m.group(1));
+ } catch (NumberFormatException ex) {
+ throw new SAXParseException("Invalid field name: '" + text + "'", locator, ex);
+ }
+ } else {
+ return null;
+ }
+ }
+
+ public static PowerProfile parse(InputStream stream) throws ParseException {
+ return (new Parser(stream)).parse();
+ }
+
+ private PowerProfile() {
+ }
+
+ public ComponentProfile getComponent(Component component) {
+ return mComponents.get(component);
+ }
+
+}
diff --git a/tools/powermodel/src/com/android/powermodel/PowerReport.java b/tools/powermodel/src/com/android/powermodel/PowerReport.java
new file mode 100644
index 0000000..76ba672
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/PowerReport.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import com.google.common.collect.ImmutableMap;
+
+/**
+ * PowerReport contains the summary of all power used on a device
+ * as reported by batterystats or statsd, based on the power profile.
+ */
+public class PowerReport {
+ private AppList<AppPower> mApps;
+ private double mTotalPowerMah;
+
+ private PowerReport() {
+ }
+
+ /**
+ * The total power used by this device for this PowerReport.
+ */
+ public double getTotalPowerMah() {
+ return mTotalPowerMah;
+ }
+
+ public List<AppPower> getAllApps() {
+ return mApps.getAllApps();
+ }
+
+ public List<AppPower> getRegularApps() {
+ return mApps.getRegularApps();
+ }
+
+ public List<AppPower> findApp(String pkg) {
+ return mApps.findApp(pkg);
+ }
+
+ public AppPower findApp(SpecialApp specialApp) {
+ return mApps.findApp(specialApp);
+ }
+
+ public static PowerReport createReport(PowerProfile profile, ActivityReport activityReport) {
+ final PowerReport.Builder powerReport = new PowerReport.Builder();
+ for (final AppActivity appActivity: activityReport.getAllApps()) {
+ final AppPower.Builder appPower = new AppPower.Builder();
+ appPower.setAttribution(appActivity.getAttribution());
+
+ for (final ImmutableMap.Entry<Component,ComponentActivity> entry:
+ appActivity.getComponentActivities().entrySet()) {
+ final ComponentPower componentPower = entry.getValue()
+ .applyProfile(activityReport, profile);
+ if (componentPower != null) {
+ appPower.addComponentPower(entry.getKey(), componentPower);
+ }
+ }
+
+ powerReport.add(appPower);
+ }
+ return powerReport.build();
+ }
+
+ private static class Builder {
+ private AppList.Builder mApps = new AppList.Builder();
+
+ public Builder() {
+ }
+
+ public PowerReport build() {
+ final PowerReport report = new PowerReport();
+
+ report.mApps = mApps.build();
+
+ for (AppPower app: report.mApps.getAllApps()) {
+ report.mTotalPowerMah += app.getAppPowerMah();
+ }
+
+ return report;
+ }
+
+ public void add(AppPower.Builder app) {
+ mApps.put(app.getAttribution(), app);
+ }
+ }
+}
diff --git a/tools/powermodel/src/com/android/powermodel/RawBatteryStats.java b/tools/powermodel/src/com/android/powermodel/RawBatteryStats.java
new file mode 100644
index 0000000..76c0482
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/RawBatteryStats.java
@@ -0,0 +1,1175 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+
+public class RawBatteryStats {
+ /**
+ * The factory objects for the records. Initialized in the static block.
+ */
+ private static HashMap<String,RecordFactory> sFactories
+ = new HashMap<String,RecordFactory>();
+
+ /**
+ * The Record objects that have been parsed.
+ */
+ private ArrayList<Record> mRecords = new ArrayList<Record>();
+
+ /**
+ * The Record objects that have been parsed, indexed by type.
+ *
+ * Don't use this before {@link #indexRecords()} has been called.
+ */
+ private ImmutableMap<String,ImmutableList<Record>> mRecordsByType;
+
+ /**
+ * The attribution keys for which we have data (corresponding to UIDs we've seen).
+ * <p>
+ * Does not include the synthetic apps.
+ * <p>
+ * Don't use this before {@link #indexRecords()} has been called.
+ */
+ private ImmutableSet<AttributionKey> mApps;
+
+ /**
+ * The warnings that have been issued during parsing.
+ */
+ private ArrayList<Warning> mWarnings = new ArrayList<Warning>();
+
+ /**
+ * The version of the BatteryStats dumpsys that we are using. This value
+ * is set to -1 initially, and then when parsing the (hopefully) first
+ * line, 'vers', it is set to the correct version.
+ */
+ private int mDumpsysVersion = -1;
+
+ /**
+ * Enum used in the Line annotation to mark whether a field is expected to be
+ * system-wide or scoped to an app.
+ */
+ public enum Scope {
+ SYSTEM,
+ UID
+ }
+
+ /**
+ * Enum used to indicated the expected number of results.
+ */
+ public enum Count {
+ SINGLE,
+ MULTIPLE
+ }
+
+ /**
+ * Annotates classes that represent a line of CSV in the batterystats CSV
+ */
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(ElementType.TYPE)
+ @interface Line {
+ String tag();
+ Scope scope();
+ Count count();
+ }
+
+ /**
+ * Annotates fields that should be parsed automatically from CSV
+ */
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(ElementType.FIELD)
+ @interface Field {
+ /**
+ * The "column" of this field in the most recent version of the CSV.
+ * When parsing old versions, fields that were added will be automatically
+ * removed and the indices will be fixed up.
+ *
+ * The header fields (version, uid, category, type) will be automatically
+ * handled for the base Line type. The index 0 should start after those.
+ */
+ int index();
+
+ /**
+ * First version that this field appears in.
+ */
+ int added() default 0;
+ }
+
+ /**
+ * Each line in the BatteryStats CSV is tagged with a category, that says
+ * which of the time collection modes was used for the data.
+ */
+ public enum Category {
+ INFO("i"),
+ LAST("l"),
+ UNPLUGGED("u"),
+ CURRENT("c");
+
+ public final String tag;
+
+ Category(String tag) {
+ this.tag = tag;
+ }
+ }
+
+ /**
+ * Base class for all lines in a batterystats CSV file.
+ */
+ public static class Record {
+ /**
+ * Whether all of the fields for the indicated version of this record
+ * have been filled in.
+ */
+ public boolean complete;
+
+
+ @Field(index=-4)
+ public int lineVersion;
+
+ @Field(index=-3)
+ public int uid;
+
+ @Field(index=-2)
+ public Category category;
+
+ @Field(index=-1)
+ public String lineType;
+ }
+
+ @Line(tag="bt", scope=Scope.SYSTEM, count=Count.SINGLE)
+ public static class Battery extends Record {
+ // If which != STATS_SINCE_CHARGED, the csv will be "N/A" and we will get
+ // a parsing warning. Nobody uses anything other than STATS_SINCE_CHARGED.
+ @Field(index=0)
+ public int startCount;
+
+ @Field(index=1)
+ public long whichBatteryRealtimeMs;
+
+ @Field(index=2)
+ public long whichBatteryUptimeMs;
+
+ @Field(index=3)
+ public long totalRealtimeMs;
+
+ @Field(index=4)
+ public long totalUptimeMs;
+
+ @Field(index=5)
+ public long getStartClockTimeMs;
+
+ @Field(index=6)
+ public long whichBatteryScreenOffRealtimeMs;
+
+ @Field(index=7)
+ public long whichBatteryScreenOffUptimeMs;
+
+ @Field(index=8)
+ public long estimatedBatteryCapacityMah;
+
+ @Field(index=9)
+ public long minLearnedBatteryCapacityMah;
+
+ @Field(index=10)
+ public long maxLearnedBatteryCapacityMah;
+
+ @Field(index=11)
+ public long screenDozeTimeMs;
+ }
+
+ @Line(tag="gn", scope=Scope.SYSTEM, count=Count.SINGLE)
+ public static class GlobalNetwork extends Record {
+ @Field(index=0)
+ public long mobileRxTotalBytes;
+
+ @Field(index=1)
+ public long mobileTxTotalBytes;
+
+ @Field(index=2)
+ public long wifiRxTotalBytes;
+
+ @Field(index=3)
+ public long wifiTxTotalBytes;
+
+ @Field(index=4)
+ public long mobileRxTotalPackets;
+
+ @Field(index=5)
+ public long mobileTxTotalPackets;
+
+ @Field(index=6)
+ public long wifiRxTotalPackets;
+
+ @Field(index=7)
+ public long wifiTxTotalPackets;
+
+ @Field(index=8)
+ public long btRxTotalBytes;
+
+ @Field(index=9)
+ public long btTxTotalBytes;
+ }
+
+ @Line(tag="gmcd", scope=Scope.SYSTEM, count=Count.SINGLE)
+ public static class GlobalModemController extends Record {
+ @Field(index=0)
+ public long idleMs;
+
+ @Field(index=1)
+ public long rxTimeMs;
+
+ @Field(index=2)
+ public long powerMaMs;
+
+ @Field(index=3)
+ public long[] txTimeMs;
+ }
+
+ @Line(tag="m", scope=Scope.SYSTEM, count=Count.SINGLE)
+ public static class Misc extends Record {
+ @Field(index=0)
+ public long screenOnTimeMs;
+
+ @Field(index=1)
+ public long phoneOnTimeMs;
+
+ @Field(index=2)
+ public long fullWakeLockTimeTotalMs;
+
+ @Field(index=3)
+ public long partialWakeLockTimeTotalMs;
+
+ @Field(index=4)
+ public long mobileRadioActiveTimeMs;
+
+ @Field(index=5)
+ public long mobileRadioActiveAdjustedTimeMs;
+
+ @Field(index=6)
+ public long interactiveTimeMs;
+
+ @Field(index=7)
+ public long powerSaveModeEnabledTimeMs;
+
+ @Field(index=8)
+ public int connectivityChangeCount;
+
+ @Field(index=9)
+ public long deepDeviceIdleModeTimeMs;
+
+ @Field(index=10)
+ public long deepDeviceIdleModeCount;
+
+ @Field(index=11)
+ public long deepDeviceIdlingTimeMs;
+
+ @Field(index=12)
+ public long deepDeviceIdlingCount;
+
+ @Field(index=13)
+ public long mobileRadioActiveCount;
+
+ @Field(index=14)
+ public long mobileRadioActiveUnknownTimeMs;
+
+ @Field(index=15)
+ public long lightDeviceIdleModeTimeMs;
+
+ @Field(index=16)
+ public long lightDeviceIdleModeCount;
+
+ @Field(index=17)
+ public long lightDeviceIdlingTimeMs;
+
+ @Field(index=18)
+ public long lightDeviceIdlingCount;
+
+ @Field(index=19)
+ public long lightLongestDeviceIdleModeTimeMs;
+
+ @Field(index=20)
+ public long deepLongestDeviceIdleModeTimeMs;
+ }
+
+ @Line(tag="nt", scope=Scope.UID, count=Count.SINGLE)
+ public static class Network extends Record {
+ @Field(index=0)
+ public long mobileRxBytes;
+
+ @Field(index=1)
+ public long mobileTxBytes;
+
+ @Field(index=2)
+ public long wifiRxBytes;
+
+ @Field(index=3)
+ public long wifiTxBytes;
+
+ @Field(index=4)
+ public long mobileRxPackets;
+
+ @Field(index=5)
+ public long mobileTxPackets;
+
+ @Field(index=6)
+ public long wifiRxPackets;
+
+ @Field(index=7)
+ public long wifiTxPackets;
+
+ // This is microseconds, because... batterystats.
+ @Field(index=8)
+ public long mobileRadioActiveTimeUs;
+
+ @Field(index=9)
+ public long mobileRadioActiveCount;
+
+ @Field(index=10)
+ public long btRxBytes;
+
+ @Field(index=11)
+ public long btTxBytes;
+
+ @Field(index=12)
+ public long mobileWakeupCount;
+
+ @Field(index=13)
+ public long wifiWakeupCount;
+
+ @Field(index=14)
+ public long mobileBgRxBytes;
+
+ @Field(index=15)
+ public long mobileBgTxBytes;
+
+ @Field(index=16)
+ public long wifiBgRxBytes;
+
+ @Field(index=17)
+ public long wifiBgTxBytes;
+
+ @Field(index=18)
+ public long mobileBgRxPackets;
+
+ @Field(index=19)
+ public long mobileBgTxPackets;
+
+ @Field(index=20)
+ public long wifiBgRxPackets;
+
+ @Field(index=21)
+ public long wifiBgTxPackets;
+ }
+
+ @Line(tag="sgt", scope=Scope.SYSTEM, count=Count.SINGLE)
+ public static class SignalStrengthTime extends Record {
+ @Field(index=0)
+ public long[] phoneSignalStrengthTimeMs;
+ }
+
+ @Line(tag="sst", scope=Scope.SYSTEM, count=Count.SINGLE)
+ public static class SignalScanningTime extends Record {
+ @Field(index=0)
+ public long phoneSignalScanningTimeMs;
+ }
+
+ @Line(tag="uid", scope=Scope.UID, count=Count.MULTIPLE)
+ public static class Uid extends Record {
+ @Field(index=0)
+ public int uidKey;
+
+ @Field(index=1)
+ public String pkg;
+ }
+
+ @Line(tag="vers", scope=Scope.SYSTEM, count=Count.SINGLE)
+ public static class Version extends Record {
+ @Field(index=0)
+ public int dumpsysVersion;
+
+ @Field(index=1)
+ public int parcelVersion;
+
+ @Field(index=2)
+ public String startPlatformVersion;
+
+ @Field(index=3)
+ public String endPlatformVersion;
+ }
+
+ /**
+ * Codes for the warnings to classify warnings without parsing them.
+ */
+ public enum WarningId {
+ /**
+ * A row didn't have enough fields to match any records and let us extract
+ * a line type.
+ */
+ TOO_FEW_FIELDS_FOR_LINE_TYPE,
+
+ /**
+ * We couldn't find a Record for the given line type.
+ */
+ NO_MATCHING_LINE_TYPE,
+
+ /**
+ * Couldn't set the value of a field. Usually this is because the
+ * contents of a numeric type couldn't be parsed.
+ */
+ BAD_FIELD_TYPE,
+
+ /**
+ * There were extra field values in the input text.
+ */
+ TOO_MANY_FIELDS,
+
+ /**
+ * There were fields that we were expecting (for this version
+ * of the dumpsys) that weren't provided in the CSV data.
+ */
+ NOT_ENOUGH_FIELDS,
+
+ /**
+ * The dumpsys version in the 'vers' CSV line couldn't be parsed.
+ */
+ BAD_DUMPSYS_VERSION
+ }
+
+ /**
+ * A non-fatal problem detected during parsing.
+ */
+ public static class Warning {
+ private int mLineNumber;
+ private WarningId mId;
+ private ArrayList<String> mFields;
+ private String mMessage;
+ private String[] mExtras;
+
+ public Warning(int lineNumber, WarningId id, ArrayList<String> fields, String message,
+ String[] extras) {
+ mLineNumber = lineNumber;
+ mId = id;
+ mFields = fields;
+ mMessage = message;
+ mExtras = extras;
+ }
+
+ public int getLineNumber() {
+ return mLineNumber;
+ }
+
+ public ArrayList<String> getFields() {
+ return mFields;
+ }
+
+ public String getMessage() {
+ return mMessage;
+ }
+
+ public String[] getExtras() {
+ return mExtras;
+ }
+ }
+
+ /**
+ * Base class for classes to set fields on Record objects via reflection.
+ */
+ private abstract static class FieldSetter {
+ private int mIndex;
+ private int mAdded;
+ protected java.lang.reflect.Field mField;
+
+ FieldSetter(int index, int added, java.lang.reflect.Field field) {
+ mIndex = index;
+ mAdded = added;
+ mField = field;
+ }
+
+ String getName() {
+ return mField.getName();
+ }
+
+ int getIndex() {
+ return mIndex;
+ }
+
+ int getAdded() {
+ return mAdded;
+ }
+
+ boolean isArray() {
+ return mField.getType().isArray();
+ }
+
+ abstract void setField(int lineNumber, Record object, String value) throws ParseException;
+ abstract void setArray(int lineNumber, Record object, ArrayList<String> values,
+ int startIndex, int endIndex) throws ParseException;
+
+ @Override
+ public String toString() {
+ final String className = getClass().getName();
+ int startIndex = Math.max(className.lastIndexOf('.'), className.lastIndexOf('$'));
+ if (startIndex < 0) {
+ startIndex = 0;
+ } else {
+ startIndex++;
+ }
+ return className.substring(startIndex) + "(index=" + mIndex + " added=" + mAdded
+ + " field=" + mField.getName()
+ + " type=" + mField.getType().getSimpleName()
+ + ")";
+ }
+ }
+
+ /**
+ * Sets int fields on Record objects using reflection.
+ */
+ private static class IntFieldSetter extends FieldSetter {
+ IntFieldSetter(int index, int added, java.lang.reflect.Field field) {
+ super(index, added, field);
+ }
+
+ void setField(int lineNumber, Record object, String value) throws ParseException {
+ try {
+ mField.setInt(object, Integer.parseInt(value.trim()));
+ } catch (NumberFormatException ex) {
+ throw new ParseException(lineNumber, "can't parse as integer: " + value);
+ } catch (IllegalAccessException | IllegalArgumentException
+ | ExceptionInInitializerError ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ void setArray(int lineNumber, Record object, ArrayList<String> values,
+ int startIndex, int endIndex) throws ParseException {
+ try {
+ final int[] array = new int[endIndex-startIndex];
+ for (int i=startIndex; i<endIndex; i++) {
+ final String value = values.get(startIndex+i);
+ try {
+ array[i] = Integer.parseInt(value.trim());
+ } catch (NumberFormatException ex) {
+ throw new ParseException(lineNumber, "can't parse field "
+ + i + " as integer: " + value);
+ }
+ }
+ mField.set(object, array);
+ } catch (IllegalAccessException | IllegalArgumentException
+ | ExceptionInInitializerError ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+ }
+
+ /**
+ * Sets long fields on Record objects using reflection.
+ */
+ private static class LongFieldSetter extends FieldSetter {
+ LongFieldSetter(int index, int added, java.lang.reflect.Field field) {
+ super(index, added, field);
+ }
+
+ void setField(int lineNumber, Record object, String value) throws ParseException {
+ try {
+ mField.setLong(object, Long.parseLong(value.trim()));
+ } catch (NumberFormatException ex) {
+ throw new ParseException(lineNumber, "can't parse as long: " + value);
+ } catch (IllegalAccessException | IllegalArgumentException
+ | ExceptionInInitializerError ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ void setArray(int lineNumber, Record object, ArrayList<String> values,
+ int startIndex, int endIndex) throws ParseException {
+ try {
+ final long[] array = new long[endIndex-startIndex];
+ for (int i=0; i<(endIndex-startIndex); i++) {
+ final String value = values.get(startIndex+i);
+ try {
+ array[i] = Long.parseLong(value.trim());
+ } catch (NumberFormatException ex) {
+ throw new ParseException(lineNumber, "can't parse field "
+ + i + " as long: " + value);
+ }
+ }
+ mField.set(object, array);
+ } catch (IllegalAccessException | IllegalArgumentException
+ | ExceptionInInitializerError ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+ }
+
+ /**
+ * Sets String fields on Record objects using reflection.
+ */
+ private static class StringFieldSetter extends FieldSetter {
+ StringFieldSetter(int index, int added, java.lang.reflect.Field field) {
+ super(index, added, field);
+ }
+
+ void setField(int lineNumber, Record object, String value) throws ParseException {
+ try {
+ mField.set(object, value);
+ } catch (IllegalAccessException | IllegalArgumentException
+ | ExceptionInInitializerError ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ void setArray(int lineNumber, Record object, ArrayList<String> values,
+ int startIndex, int endIndex) throws ParseException {
+ try {
+ final String[] array = new String[endIndex-startIndex];
+ for (int i=0; i<(endIndex-startIndex); i++) {
+ array[i] = values.get(startIndex+1);
+ }
+ mField.set(object, array);
+ } catch (IllegalAccessException | IllegalArgumentException
+ | ExceptionInInitializerError ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+ }
+
+ /**
+ * Sets enum fields on Record objects using reflection.
+ *
+ * To be parsed automatically, enums must have a public final String tag
+ * field, which is the string that will appear in the csv for that enum value.
+ */
+ private static class EnumFieldSetter extends FieldSetter {
+ private final HashMap<String,Enum> mTags = new HashMap<String,Enum>();
+
+ EnumFieldSetter(int index, int added, java.lang.reflect.Field field) {
+ super(index, added, field);
+
+ // Build the mapping of tags to values.
+ final Class<?> fieldType = field.getType();
+
+ java.lang.reflect.Field tagField = null;
+ try {
+ tagField = fieldType.getField("tag");
+ } catch (NoSuchFieldException ex) {
+ throw new RuntimeException("Missing tag field."
+ + " To be parsed automatically, enums must have"
+ + " a String field called tag. Enum class: " + fieldType.getName()
+ + " Containing class: " + field.getDeclaringClass().getName()
+ + " Field: " + field.getName());
+
+ }
+ if (!String.class.equals(tagField.getType())) {
+ throw new RuntimeException("Tag field is not string."
+ + " To be parsed automatically, enums must have"
+ + " a String field called tag. Enum class: " + fieldType.getName()
+ + " Containing class: " + field.getDeclaringClass().getName()
+ + " Field: " + field.getName()
+ + " Tag field type: " + tagField.getType().getName());
+ }
+
+ for (final Object enumValue: fieldType.getEnumConstants()) {
+ String tag = null;
+ try {
+ tag = (String)tagField.get(enumValue);
+ } catch (IllegalAccessException | IllegalArgumentException
+ | ExceptionInInitializerError ex) {
+ throw new RuntimeException(ex);
+ }
+ mTags.put(tag, (Enum)enumValue);
+ }
+ }
+
+ void setField(int lineNumber, Record object, String value) throws ParseException {
+ final Enum enumValue = mTags.get(value);
+ if (enumValue == null) {
+ throw new ParseException(lineNumber, "Could not find enum for field "
+ + getName() + " for tag: " + value);
+ }
+ try {
+ mField.set(object, enumValue);
+ } catch (IllegalAccessException | IllegalArgumentException
+ | ExceptionInInitializerError ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ void setArray(int lineNumber, Record object, ArrayList<String> values,
+ int startIndex, int endIndex) throws ParseException {
+ try {
+ final Object array = Array.newInstance(mField.getType().getComponentType(),
+ endIndex-startIndex);
+ for (int i=0; i<(endIndex-startIndex); i++) {
+ final String value = values.get(startIndex+i);
+ final Enum enumValue = mTags.get(value);
+ if (enumValue == null) {
+ throw new ParseException(lineNumber, "Could not find enum for field "
+ + getName() + " for tag: " + value);
+ }
+ Array.set(array, i, enumValue);
+ }
+ mField.set(object, array);
+ } catch (IllegalAccessException | IllegalArgumentException
+ | ExceptionInInitializerError ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+ }
+
+ /**
+ * Factory for the record classes. Uses reflection to create
+ * the fields.
+ */
+ private static class RecordFactory {
+ private String mTag;
+ private Class<? extends Record> mSubclass;
+ private ArrayList<FieldSetter> mFieldSetters;
+
+ RecordFactory(String tag, Class<? extends Record> subclass,
+ ArrayList<FieldSetter> fieldSetters) {
+ mTag = tag;
+ mSubclass = subclass;
+ mFieldSetters = fieldSetters;
+ }
+
+ /**
+ * Create an object of one of the subclasses of Record, and fill
+ * in the fields marked with the Field annotation.
+ *
+ * @return a new Record with the fields filled in. If there are missing
+ * fields, the {@link Record.complete} field will be set to false.
+ */
+ Record create(RawBatteryStats bs, int dumpsysVersion, int lineNumber,
+ ArrayList<String> fieldValues) {
+ final boolean debug = false;
+ Record record = null;
+ try {
+ if (debug) {
+ System.err.println("Creating object: " + mSubclass.getSimpleName());
+ }
+ record = mSubclass.newInstance();
+ } catch (IllegalAccessException | InstantiationException
+ | ExceptionInInitializerError | SecurityException ex) {
+ throw new RuntimeException("Exception creating " + mSubclass.getName()
+ + " for '" + mTag + "' record.", ex);
+ }
+ record.complete = true;
+ int fieldIndex = 0;
+ int setterIndex = 0;
+ while (fieldIndex < fieldValues.size() && setterIndex < mFieldSetters.size()) {
+ final FieldSetter setter = mFieldSetters.get(setterIndex);
+
+ if (dumpsysVersion >= 0 && dumpsysVersion < setter.getAdded()) {
+ // The version being parsed doesn't have the field for this setter,
+ // so skip the setter but not the field.
+ setterIndex++;
+ continue;
+ }
+
+ final String value = fieldValues.get(fieldIndex);
+ try {
+ if (debug) {
+ System.err.println(" setting field " + setter + " to: " + value);
+ }
+ if (setter.isArray()) {
+ setter.setArray(lineNumber, record, fieldValues,
+ fieldIndex, fieldValues.size());
+ // The rest of the fields have been consumed.
+ fieldIndex = fieldValues.size();
+ setterIndex = mFieldSetters.size();
+ break;
+ } else {
+ setter.setField(lineNumber, record, value);
+ }
+ } catch (ParseException ex) {
+ bs.addWarning(lineNumber, WarningId.BAD_FIELD_TYPE, fieldValues,
+ ex.getMessage(), mTag, value);
+ record.complete = false;
+ }
+
+ fieldIndex++;
+ setterIndex++;
+ }
+
+ // If there are extra fields, this record is complete, there are just
+ // extra values, so we issue a warning but don't mark it incomplete.
+ if (fieldIndex < fieldValues.size()) {
+ bs.addWarning(lineNumber, WarningId.TOO_MANY_FIELDS, fieldValues,
+ "Line '" + mTag + "' has extra fields.",
+ mTag, Integer.toString(fieldIndex), Integer.toString(fieldValues.size()));
+ if (debug) {
+ for (int i=0; i<mFieldSetters.size(); i++) {
+ System.err.println(" setter: [" + i + "] " + mFieldSetters.get(i));
+ }
+ }
+ }
+
+ // If we have any fields that are missing, add a warning and return null.
+ for (; setterIndex < mFieldSetters.size(); setterIndex++) {
+ final FieldSetter setter = mFieldSetters.get(setterIndex);
+ if (dumpsysVersion >= 0 && dumpsysVersion >= setter.getAdded()) {
+ bs.addWarning(lineNumber, WarningId.NOT_ENOUGH_FIELDS, fieldValues,
+ "Line '" + mTag + "' missing field: index=" + setterIndex
+ + " name=" + setter.getName(),
+ mTag, Integer.toString(setterIndex));
+ record.complete = false;
+ }
+ }
+
+ return record;
+ }
+ }
+
+ /**
+ * Parse the input stream and return a RawBatteryStats object.
+ */
+ public static RawBatteryStats parse(InputStream input) throws ParseException, IOException {
+ final RawBatteryStats result = new RawBatteryStats();
+ result.parseImpl(input);
+ return result;
+ }
+
+ /**
+ * Get a record.
+ * <p>
+ * If multiple of that record are found, returns the first one. There will already
+ * have been a warning recorded if the count annotation did not match what was in the
+ * csv.
+ * <p>
+ * Returns null if there are no records of that type.
+ */
+ public <T extends Record> T getSingle(Class<T> cl) {
+ final List<Record> list = mRecordsByType.get(cl.getName());
+ if (list == null) {
+ return null;
+ }
+ // Notes:
+ // - List can never be empty because the list itself wouldn't have been added.
+ // - Cast is safe because list was populated based on class name (let's assume
+ // there's only one class loader involved here).
+ return (T)list.get(0);
+ }
+
+ /**
+ * Get a record.
+ * <p>
+ * If multiple of that record are found, returns the first one that matches that uid.
+ * <p>
+ * Returns null if there are no records of that type that match the given uid.
+ */
+ public <T extends Record> T getSingle(Class<T> cl, int uid) {
+ final List<Record> list = mRecordsByType.get(cl.getName());
+ if (list == null) {
+ return null;
+ }
+ for (final Record record: list) {
+ if (record.uid == uid) {
+ // Cast is safe because list was populated based on class name (let's assume
+ // there's only one class loader involved here).
+ return (T)record;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Get all the records of the given type.
+ */
+ public <T extends Record> List<T> getMultiple(Class<T> cl) {
+ final List<Record> list = mRecordsByType.get(cl.getName());
+ if (list == null) {
+ return ImmutableList.<T>of();
+ }
+ // Cast is safe because list was populated based on class name (let's assume
+ // there's only one class loader involved here).
+ return ImmutableList.copyOf((List<T>)list);
+ }
+
+ /**
+ * Get the UIDs that are covered by this batterystats dump.
+ */
+ public Set<AttributionKey> getApps() {
+ return mApps;
+ }
+
+ /**
+ * No public constructor. Use {@link #parse}.
+ */
+ private RawBatteryStats() {
+ }
+
+ /**
+ * Get the list of Record objects that were parsed from the csv.
+ */
+ public List<Record> getRecords() {
+ return mRecords;
+ }
+
+ /**
+ * Gets the warnings that were encountered during parsing.
+ */
+ public List<Warning> getWarnings() {
+ return mWarnings;
+ }
+
+ /**
+ * Implementation of the csv parsing.
+ */
+ private void parseImpl(InputStream input) throws ParseException, IOException {
+ // Parse the csv
+ CsvParser.parse(input, new CsvParser.LineProcessor() {
+ @Override
+ public void onLine(int lineNumber, ArrayList<String> fields)
+ throws ParseException {
+ handleCsvLine(lineNumber, fields);
+ }
+ });
+
+ // Gather the records by class name for the getSingle() and getMultiple() functions.
+ indexRecords();
+
+ // Gather the uids from all the places UIDs come from, for getApps().
+ indexApps();
+ }
+
+ /**
+ * Handle a line of CSV input, creating the right Record object.
+ */
+ private void handleCsvLine(int lineNumber, ArrayList<String> fields) throws ParseException {
+ // The standard rows all have the 4 core fields. Anything less isn't what we're
+ // looking for.
+ if (fields.size() <= 4) {
+ addWarning(lineNumber, WarningId.TOO_FEW_FIELDS_FOR_LINE_TYPE, fields,
+ "Line with too few fields (" + fields.size() + ")",
+ Integer.toString(fields.size()));
+ return;
+ }
+
+ final String lineType = fields.get(3);
+
+ // Handle the vers line specially, because we need the version number
+ // to make the rest of the machinery work.
+ if ("vers".equals(lineType)) {
+ final String versionText = fields.get(4);
+ try {
+ mDumpsysVersion = Integer.parseInt(versionText);
+ } catch (NumberFormatException ex) {
+ addWarning(lineNumber, WarningId.BAD_DUMPSYS_VERSION, fields,
+ "Couldn't parse dumpsys version number: '" + versionText,
+ versionText);
+ }
+ }
+
+ // Find the right factory.
+ final RecordFactory factory = sFactories.get(lineType);
+ if (factory == null) {
+ addWarning(lineNumber, WarningId.NO_MATCHING_LINE_TYPE, fields,
+ "No Record for line type '" + lineType + "'",
+ lineType);
+ return;
+ }
+
+ // Create the record.
+ final Record record = factory.create(this, mDumpsysVersion, lineNumber, fields);
+ mRecords.add(record);
+ }
+
+ /**
+ * Add to the list of warnings.
+ */
+ private void addWarning(int lineNumber, WarningId id,
+ ArrayList<String> fields, String message, String... extras) {
+ mWarnings.add(new Warning(lineNumber, id, fields, message, extras));
+ final boolean debug = false;
+ if (debug) {
+ final StringBuilder text = new StringBuilder("line ");
+ text.append(lineNumber);
+ text.append(": WARNING: ");
+ text.append(message);
+ text.append("\n fields: ");
+ for (int i=0; i<fields.size(); i++) {
+ final String field = fields.get(i);
+ if (field.indexOf('"') >= 0) {
+ text.append('"');
+ text.append(field.replace("\"", "\"\""));
+ text.append('"');
+ } else {
+ text.append(field);
+ }
+ if (i != fields.size() - 1) {
+ text.append(',');
+ }
+ }
+ text.append('\n');
+ for (String extra: extras) {
+ text.append(" extra: ");
+ text.append(extra);
+ text.append('\n');
+ }
+ System.err.print(text.toString());
+ }
+ }
+
+ /**
+ * Group records by class name.
+ */
+ private void indexRecords() {
+ final HashMap<String,ArrayList<Record>> map = new HashMap<String,ArrayList<Record>>();
+
+ // Iterate over all of the records
+ for (Record record: mRecords) {
+ final String className = record.getClass().getName();
+
+ ArrayList<Record> list = map.get(className);
+ if (list == null) {
+ list = new ArrayList<Record>();
+ map.put(className, list);
+ }
+
+ list.add(record);
+ }
+
+ // Make it immutable
+ final HashMap<String,ImmutableList<Record>> result
+ = new HashMap<String,ImmutableList<Record>>();
+ for (HashMap.Entry<String,ArrayList<Record>> entry: map.entrySet()) {
+ result.put(entry.getKey(), ImmutableList.copyOf(entry.getValue()));
+ }
+
+ // Initialize here so uninitialized access will result in NPE.
+ mRecordsByType = ImmutableMap.copyOf(result);
+ }
+
+ /**
+ * Collect the UIDs from the csv.
+ *
+ * They come from two places.
+ * <ul>
+ * <li>The uid to package name map entries ({@link #Uid}) at the beginning.
+ * <li>The uid fields of the rest of the entries, some of which might not
+ * have package names associated with them.
+ * </ul>
+ *
+ * TODO: Is this where we should also do the logic about the special UIDs?
+ */
+ private void indexApps() {
+ final HashMap<Integer,HashSet<String>> uids = new HashMap<Integer,HashSet<String>>();
+
+ // The Uid rows, from which we get package names
+ for (Uid record: getMultiple(Uid.class)) {
+ HashSet<String> list = uids.get(record.uidKey);
+ if (list == null) {
+ list = new HashSet<String>();
+ uids.put(record.uidKey, list);
+ }
+ list.add(record.pkg);
+ }
+
+ // The uid fields of everything
+ for (Record record: mRecords) {
+ // The 0 in the INFO records isn't really root, it's just unfilled data.
+ // The root uid (0) will show up practically in every record, but don't force it.
+ if (record.category != Category.INFO) {
+ if (uids.get(record.uid) == null) {
+ // There is no other data about this UID, but it does exist!
+ uids.put(record.uid, new HashSet<String>());
+ }
+ }
+ }
+
+ // Turn our temporary lists of package names into AttributionKeys.
+ final HashSet<AttributionKey> result = new HashSet<AttributionKey>();
+ for (HashMap.Entry<Integer,HashSet<String>> entry: uids.entrySet()) {
+ result.add(new AttributionKey(entry.getKey(), entry.getValue()));
+ }
+
+ // Initialize here so uninitialized access will result in NPE.
+ mApps = ImmutableSet.copyOf(result);
+ }
+
+ /**
+ * Init the factory classes.
+ */
+ static {
+ for (Class<?> cl: RawBatteryStats.class.getClasses()) {
+ final Line lineAnnotation = cl.getAnnotation(Line.class);
+ if (lineAnnotation != null && Record.class.isAssignableFrom(cl)) {
+ final ArrayList<FieldSetter> fieldSetters = new ArrayList<FieldSetter>();
+
+ for (java.lang.reflect.Field field: cl.getFields()) {
+ final Field fa = field.getAnnotation(Field.class);
+ if (fa != null) {
+ final Class<?> fieldType = field.getType();
+ final Class<?> innerType = fieldType.isArray()
+ ? fieldType.getComponentType()
+ : fieldType;
+ if (Integer.TYPE.equals(innerType)) {
+ fieldSetters.add(new IntFieldSetter(fa.index(), fa.added(), field));
+ } else if (Long.TYPE.equals(innerType)) {
+ fieldSetters.add(new LongFieldSetter(fa.index(), fa.added(), field));
+ } else if (String.class.equals(innerType)) {
+ fieldSetters.add(new StringFieldSetter(fa.index(), fa.added(), field));
+ } else if (innerType.isEnum()) {
+ fieldSetters.add(new EnumFieldSetter(fa.index(), fa.added(), field));
+ } else {
+ throw new RuntimeException("Unsupported field type '"
+ + fieldType.getName() + "' on "
+ + cl.getName() + "." + field.getName());
+ }
+ }
+ }
+ // Sort by index
+ Collections.sort(fieldSetters, new Comparator<FieldSetter>() {
+ @Override
+ public int compare(FieldSetter a, FieldSetter b) {
+ return a.getIndex() - b.getIndex();
+ }
+ });
+ // Only the last one can be an array
+ for (int i=0; i<fieldSetters.size()-1; i++) {
+ if (fieldSetters.get(i).isArray()) {
+ throw new RuntimeException("Only the last (highest index) @Field"
+ + " in class " + cl.getName() + " can be an array: "
+ + fieldSetters.get(i).getName());
+ }
+ }
+ // Add to the map
+ sFactories.put(lineAnnotation.tag(), new RecordFactory(lineAnnotation.tag(),
+ (Class<Record>)cl, fieldSetters));
+ }
+ }
+ }
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/SpecialApp.java b/tools/powermodel/src/com/android/powermodel/SpecialApp.java
new file mode 100644
index 0000000..df1e1fb
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/SpecialApp.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel;
+
+/**
+ * Identifiers for well-known apps that have unique characteristics.
+ *
+ * @more
+ * This includes three categories:
+ * <ul>
+ * <li><b>Built-in system components</b> – These have predefined UIDs that are
+ * always the same. For example, the system UID is always 1000.</li>
+ * <li><b>Well known apps with shared UIDs</b> – These do not have predefined
+ * UIDs (i.e. are different on each device), but since they have shared UIDs
+ * with varying sets of package names (GmsCore is the canonical example), we
+ * have special logic to capture these into a single entity with a well defined
+ * key. These have the {@link #uid uid} field set to
+ * {@link Uid#UID_VARIES Uid.UID_VARIES}.</li>
+ * <li><b>Synthetic remainder app</b> – The {@link #REMAINDER REMAINDER} app doesn't
+ * represent a real app. It contains accounting for usage which is not attributed
+ * to any UID. This app has the {@link #uid uid} field set to
+ * {@link Uid#UID_SYNTHETIC Uid.UID_SYNTHETIC}.</li>
+ * </ul>
+ */
+public enum SpecialApp {
+
+ /**
+ * Synthetic app that accounts for the remaining amount of resources used
+ * that is unaccounted for by apps, or overcounted because of inaccuracies
+ * in the model.
+ */
+ REMAINDER(Uid.UID_SYNTHETIC),
+
+ /**
+ * Synthetic app that holds system-wide numbers, for example the total amount
+ * of various resources used, device-wide.
+ */
+ GLOBAL(Uid.UID_SYNTHETIC),
+
+ SYSTEM(1000),
+
+ GOOGLE_SERVICES(Uid.UID_VARIES);
+
+ /**
+ * Constants for SpecialApps where the uid is not actually a UID.
+ */
+ public static class Uid {
+ /**
+ * Constant to indicate that this special app does not have a fixed UID.
+ */
+ public static final int UID_VARIES = -1;
+
+ /**
+ * Constant to indicate that this special app is not actually an app with a UID.
+ *
+ * @see SpecialApp#REMAINDER
+ * @see SpecialApp#GLOBAL
+ */
+ public static final int UID_SYNTHETIC = -2;
+ }
+
+ /**
+ * The fixed UID value of this special app, or {@link #UID_VARIES} if there
+ * isn't one.
+ */
+ public final int uid;
+
+ private SpecialApp(int uid) {
+ this.uid = uid;
+ }
+}
diff --git a/tools/powermodel/src/com/android/powermodel/component/AudioProfile.java b/tools/powermodel/src/com/android/powermodel/component/AudioProfile.java
new file mode 100644
index 0000000..63ff3a6
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/component/AudioProfile.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel.component;
+
+import java.util.Arrays;
+
+import com.android.powermodel.ComponentProfile;
+import com.android.powermodel.ParseException;
+
+public class AudioProfile extends ComponentProfile {
+ public float onMa;
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/component/BluetoothProfile.java b/tools/powermodel/src/com/android/powermodel/component/BluetoothProfile.java
new file mode 100644
index 0000000..8f5e7d0
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/component/BluetoothProfile.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel.component;
+
+import java.util.Arrays;
+
+import com.android.powermodel.ComponentProfile;
+import com.android.powermodel.ParseException;
+
+public class BluetoothProfile extends ComponentProfile {
+ public float idleMa;
+ public float rxMa;
+ public float txMa;
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/component/CameraProfile.java b/tools/powermodel/src/com/android/powermodel/component/CameraProfile.java
new file mode 100644
index 0000000..8ee22d0
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/component/CameraProfile.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel.component;
+
+import java.util.Arrays;
+
+import com.android.powermodel.ComponentProfile;
+import com.android.powermodel.ParseException;
+
+public class CameraProfile extends ComponentProfile {
+ public float onMa;
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/component/CpuProfile.java b/tools/powermodel/src/com/android/powermodel/component/CpuProfile.java
new file mode 100644
index 0000000..0b34fc8
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/component/CpuProfile.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel.component;
+
+import java.util.Arrays;
+import java.util.HashMap;
+
+import com.android.powermodel.ComponentProfile;
+import com.android.powermodel.ParseException;
+
+public class CpuProfile extends ComponentProfile {
+ public float suspendMa;
+ public float idleMa;
+ public float activeMa;
+ public Cluster[] clusters;
+
+ public static class Cluster {
+ public int coreCount;
+ public float onMa;
+ public Frequency[] frequencies;
+ }
+
+ public static class Frequency {
+ public int speedHz;
+ public float onMa;
+ }
+
+ public static class Builder {
+ private float mSuspendMa;
+ private float mIdleMa;
+ private float mActiveMa;
+ private int[] mCoreCount;
+ private HashMap<Integer,Float> mClusterOnPower = new HashMap<Integer,Float>();
+ private HashMap<Integer,int[]> mCoreSpeeds = new HashMap<Integer,int[]>();
+ private HashMap<Integer,float[]> mCorePower = new HashMap<Integer,float[]>();
+
+ public Builder() {
+ }
+
+ public void setSuspendMa(float value) throws ParseException {
+ mSuspendMa = value;
+ }
+
+ public void setIdleMa(float value) throws ParseException {
+ mIdleMa = value;
+ }
+
+ public void setActiveMa(float value) throws ParseException {
+ mActiveMa = value;
+ }
+
+ public void setCoreCount(int[] value) throws ParseException {
+ mCoreCount = Arrays.copyOf(value, value.length);
+ }
+
+ public void setClusterPower(int cluster, float value) throws ParseException {
+ mClusterOnPower.put(cluster, value);
+ }
+
+ public void setCoreSpeeds(int cluster, int[] value) throws ParseException {
+ mCoreSpeeds.put(cluster, Arrays.copyOf(value, value.length));
+ float[] power = mCorePower.get(cluster);
+ if (power != null && value.length != power.length) {
+ throw new ParseException("length of cpu.core_speeds.cluster" + cluster
+ + " (" + value.length + ") is different from length of"
+ + " cpu.core_power.cluster" + cluster + " (" + power.length + ")");
+ }
+ if (mCoreCount != null && cluster >= mCoreCount.length) {
+ throw new ParseException("cluster " + cluster
+ + " in cpu.core_speeds.cluster" + cluster
+ + " is larger than the number of clusters specified in cpu.clusters.cores ("
+ + mCoreCount.length + ")");
+ }
+ }
+
+ public void setCorePower(int cluster, float[] value) throws ParseException {
+ mCorePower.put(cluster, Arrays.copyOf(value, value.length));
+ int[] speeds = mCoreSpeeds.get(cluster);
+ if (speeds != null && value.length != speeds.length) {
+ throw new ParseException("length of cpu.core_power.cluster" + cluster
+ + " (" + value.length + ") is different from length of"
+ + " cpu.clusters.cores" + cluster + " (" + speeds.length + ")");
+ }
+ if (mCoreCount != null && cluster >= mCoreCount.length) {
+ throw new ParseException("cluster " + cluster
+ + " in cpu.core_power.cluster" + cluster
+ + " is larger than the number of clusters specified in cpu.clusters.cores ("
+ + mCoreCount.length + ")");
+ }
+ }
+
+ public CpuProfile build() throws ParseException {
+ final CpuProfile result = new CpuProfile();
+
+ // Validate cluster count
+
+ // All null or none null
+ // TODO
+
+ // Same size
+ // TODO
+
+ // No gaps
+ // TODO
+
+ // Fill in values
+ result.suspendMa = mSuspendMa;
+ result.idleMa = mIdleMa;
+ result.activeMa = mActiveMa;
+ if (mCoreCount != null) {
+ result.clusters = new Cluster[mCoreCount.length];
+ for (int i = 0; i < result.clusters.length; i++) {
+ final Cluster cluster = result.clusters[i] = new Cluster();
+ cluster.coreCount = mCoreCount[i];
+ cluster.onMa = mClusterOnPower.get(i);
+ int[] speeds = mCoreSpeeds.get(i);
+ float[] power = mCorePower.get(i);
+ cluster.frequencies = new Frequency[speeds.length];
+ for (int j = 0; j < speeds.length; j++) {
+ final Frequency freq = cluster.frequencies[j] = new Frequency();
+ freq.speedHz = speeds[j];
+ freq.onMa = power[j];
+ }
+ }
+ }
+
+ return result;
+ }
+ }
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/component/FlashlightProfile.java b/tools/powermodel/src/com/android/powermodel/component/FlashlightProfile.java
new file mode 100644
index 0000000..c85f3ff
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/component/FlashlightProfile.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel.component;
+
+import java.util.Arrays;
+
+import com.android.powermodel.ComponentProfile;
+import com.android.powermodel.ParseException;
+
+public class FlashlightProfile extends ComponentProfile {
+ public float onMa;
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/component/GpsProfile.java b/tools/powermodel/src/com/android/powermodel/component/GpsProfile.java
new file mode 100644
index 0000000..83c06a7
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/component/GpsProfile.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel.component;
+
+import java.util.Arrays;
+
+import com.android.powermodel.ComponentProfile;
+import com.android.powermodel.ParseException;
+
+public class GpsProfile extends ComponentProfile {
+ public float onMa;
+ public float[] signalQualityMa;
+
+ public static class Builder {
+ private float onMa;
+ private float[] mSignalQualityMa;
+
+ public Builder() {
+ }
+
+ public void setOnMa(float value) throws ParseException {
+ onMa = value;
+ }
+
+ public void setSignalMa(float[] value) throws ParseException {
+ mSignalQualityMa = value;
+ }
+
+ public GpsProfile build() throws ParseException {
+ GpsProfile result = new GpsProfile();
+ result.onMa = onMa;
+ result.signalQualityMa = mSignalQualityMa == null
+ ? new float[0]
+ : Arrays.copyOf(mSignalQualityMa, mSignalQualityMa.length);
+ return result;
+ }
+ }
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/component/ModemAppActivity.java b/tools/powermodel/src/com/android/powermodel/component/ModemAppActivity.java
new file mode 100644
index 0000000..cb70051
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/component/ModemAppActivity.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel.component;
+
+import com.android.powermodel.ActivityReport;
+import com.android.powermodel.AttributionKey;
+import com.android.powermodel.Component;
+import com.android.powermodel.ComponentActivity;
+import com.android.powermodel.PowerProfile;
+import com.android.powermodel.util.Conversion;
+
+/**
+ * Encapsulates the work done by the celluar modem on behalf of an app.
+ */
+public class ModemAppActivity extends ComponentActivity {
+ /**
+ * Construct a new ModemAppActivity.
+ */
+ public ModemAppActivity(AttributionKey attribution) {
+ super(attribution);
+ }
+
+ /**
+ * The number of packets received by the app.
+ */
+ public long rxPacketCount;
+
+ /**
+ * The number of packets sent by the app.
+ */
+ public long txPacketCount;
+
+ @Override
+ public ModemAppPower applyProfile(ActivityReport activityReport, PowerProfile profile) {
+ // Profile
+ final ModemProfile modemProfile = (ModemProfile)profile.getComponent(Component.MODEM);
+ if (modemProfile == null) {
+ // TODO: This is kind of a big problem... Should this throw instead?
+ return null;
+ }
+
+ // Activity
+ final ModemGlobalActivity global
+ = (ModemGlobalActivity)activityReport.findGlobalComponent(Component.MODEM);
+ if (global == null) {
+ return null;
+ }
+
+ final double averageModemPowerMa = getAverageModemPowerMa(modemProfile);
+ final long totalPacketCount = global.rxPacketCount + global.txPacketCount;
+ final long appPacketCount = this.rxPacketCount + this.txPacketCount;
+
+ final ModemAppPower result = new ModemAppPower();
+ result.attribution = this.attribution;
+ result.activity = this;
+ result.powerMah = Conversion.msToHr(
+ (totalPacketCount > 0 ? (appPacketCount / (double)totalPacketCount) : 0)
+ * global.totalActiveTimeMs
+ * averageModemPowerMa);
+ return result;
+ }
+
+ static final double getAverageModemPowerMa(ModemProfile profile) {
+ double sumMa = profile.getRxMa();
+ for (float powerAtTxLevelMa: profile.getTxMa()) {
+ sumMa += powerAtTxLevelMa;
+ }
+ return sumMa / (profile.getTxMa().length + 1);
+ }
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/component/ModemAppPower.java b/tools/powermodel/src/com/android/powermodel/component/ModemAppPower.java
new file mode 100644
index 0000000..f553127
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/component/ModemAppPower.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel.component;
+
+import com.android.powermodel.Component;
+import com.android.powermodel.ComponentPower;
+
+public class ModemAppPower extends ComponentPower<ModemAppActivity> {
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/component/ModemBatteryStatsReader.java b/tools/powermodel/src/com/android/powermodel/component/ModemBatteryStatsReader.java
new file mode 100644
index 0000000..6dbfbc2
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/component/ModemBatteryStatsReader.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel.component;
+
+import java.util.ArrayList;
+import java.util.List;
+import com.android.powermodel.AttributionKey;
+import com.android.powermodel.ComponentActivity;
+import com.android.powermodel.RawBatteryStats;
+import com.android.powermodel.SpecialApp;
+
+public class ModemBatteryStatsReader {
+ private ModemBatteryStatsReader() {
+ }
+
+ public static List<ComponentActivity> createActivities(RawBatteryStats bs) {
+ final List<ComponentActivity> result = new ArrayList<ComponentActivity>();
+
+ // The whole system
+ createGlobal(result, bs);
+
+ // The apps
+ createApps(result, bs);
+
+ // The synthetic "cell" app.
+ createRemainder(result, bs);
+
+ return result;
+ }
+
+ private static void createGlobal(List<ComponentActivity> result, RawBatteryStats bs) {
+ final ModemGlobalActivity global
+ = new ModemGlobalActivity(new AttributionKey(SpecialApp.GLOBAL));
+
+ final RawBatteryStats.GlobalNetwork gn = bs.getSingle(RawBatteryStats.GlobalNetwork.class);
+ final RawBatteryStats.Misc misc = bs.getSingle(RawBatteryStats.Misc.class);
+
+ // Null here just means no network activity.
+ if (gn != null && misc != null) {
+ global.rxPacketCount = gn.mobileRxTotalPackets;
+ global.txPacketCount = gn.mobileTxTotalPackets;
+
+ global.totalActiveTimeMs = misc.mobileRadioActiveTimeMs;
+ }
+
+ result.add(global);
+ }
+
+ private static void createApps(List<ComponentActivity> result, RawBatteryStats bs) {
+ for (AttributionKey key: bs.getApps()) {
+ final int uid = key.getUid();
+ final RawBatteryStats.Network network
+ = bs.getSingle(RawBatteryStats.Network.class, uid);
+
+ // Null here just means no network activity.
+ if (network != null) {
+ final ModemAppActivity app = new ModemAppActivity(key);
+
+ app.rxPacketCount = network.mobileRxPackets;
+ app.txPacketCount = network.mobileTxPackets;
+
+ result.add(app);
+ }
+ }
+ }
+
+ private static void createRemainder(List<ComponentActivity> result, RawBatteryStats bs) {
+ final RawBatteryStats.SignalStrengthTime strength
+ = bs.getSingle(RawBatteryStats.SignalStrengthTime.class);
+ final RawBatteryStats.SignalScanningTime scanning
+ = bs.getSingle(RawBatteryStats.SignalScanningTime.class);
+ final RawBatteryStats.Misc misc = bs.getSingle(RawBatteryStats.Misc.class);
+
+ if (strength != null && scanning != null && misc != null) {
+ final ModemRemainderActivity remainder
+ = new ModemRemainderActivity(new AttributionKey(SpecialApp.REMAINDER));
+
+ // Signal strength buckets
+ remainder.strengthTimeMs = strength.phoneSignalStrengthTimeMs;
+
+ // Time spent scanning
+ remainder.scanningTimeMs = scanning.phoneSignalScanningTimeMs;
+
+ // Unaccounted for active time
+ final long totalActiveTimeMs = misc.mobileRadioActiveTimeMs;
+ long appActiveTimeMs = 0;
+ for (RawBatteryStats.Network nw: bs.getMultiple(RawBatteryStats.Network.class)) {
+ appActiveTimeMs += nw.mobileRadioActiveTimeUs / 1000;
+ }
+ remainder.activeTimeMs = totalActiveTimeMs - appActiveTimeMs;
+
+ result.add(remainder);
+ }
+ }
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/component/ModemGlobalActivity.java b/tools/powermodel/src/com/android/powermodel/component/ModemGlobalActivity.java
new file mode 100644
index 0000000..a53b53e
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/component/ModemGlobalActivity.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel.component;
+
+import com.android.powermodel.ActivityReport;
+import com.android.powermodel.AttributionKey;
+import com.android.powermodel.ComponentActivity;
+import com.android.powermodel.ComponentPower;
+import com.android.powermodel.PowerProfile;
+
+/**
+ * Encapsulates total work done by the modem for the device.
+ */
+public class ModemGlobalActivity extends ComponentActivity {
+ /**
+ * Construct a new ModemGlobalActivity.
+ */
+ public ModemGlobalActivity(AttributionKey attribution) {
+ super(attribution);
+ }
+
+ /**
+ * Returns the total number of packets received in the whole device.
+ */
+ public long rxPacketCount;
+
+ /**
+ * Returns the total number of packets sent in the whole device.
+ */
+ public long txPacketCount;
+
+ /**
+ * Returns the total time the radio was active in the whole device.
+ */
+ public long totalActiveTimeMs;
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/component/ModemProfile.java b/tools/powermodel/src/com/android/powermodel/component/ModemProfile.java
new file mode 100644
index 0000000..cda72ee
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/component/ModemProfile.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel.component;
+
+import java.util.Arrays;
+
+import com.android.powermodel.ComponentProfile;
+import com.android.powermodel.ParseException;
+
+public class ModemProfile extends ComponentProfile {
+ public float sleepMa;
+ public float idleMa;
+ public float scanningMa;
+ public float rxMa;
+ public float[] txMa;
+
+ public float getSleepMa() {
+ return sleepMa;
+ }
+
+ public float getIdleMa() {
+ return idleMa;
+ }
+
+ public float getRxMa() {
+ return rxMa;
+ }
+
+ public float[] getTxMa() {
+ return Arrays.copyOf(txMa, txMa.length);
+ }
+
+ public float getScanningMa() {
+ return scanningMa;
+ }
+
+ public static class Builder {
+ private float mSleepMa;
+ private float mIdleMa;
+ private float mRxMa;
+ private float[] mTxMa;
+ private float mScanningMa;
+
+ public Builder() {
+ }
+
+ public void setSleepMa(float value) throws ParseException {
+ mSleepMa = value;
+ }
+
+ public void setIdleMa(float value) throws ParseException {
+ mIdleMa = value;
+ }
+
+ public void setRxMa(float value) throws ParseException {
+ mRxMa = value;
+ }
+
+ public void setTxMa(float[] value) throws ParseException {
+ mTxMa = Arrays.copyOf(value, value.length);
+ }
+
+ public void setScanningMa(float value) throws ParseException {
+ mScanningMa = value;
+ }
+
+ public ModemProfile build() throws ParseException {
+ ModemProfile result = new ModemProfile();
+ result.sleepMa = mSleepMa;
+ result.idleMa = mIdleMa;
+ result.rxMa = mRxMa;
+ result.txMa = mTxMa == null ? new float[0] : mTxMa;
+ result.scanningMa = mScanningMa;
+ return result;
+ }
+ }
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/component/ModemRemainderActivity.java b/tools/powermodel/src/com/android/powermodel/component/ModemRemainderActivity.java
new file mode 100644
index 0000000..0e268c2
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/component/ModemRemainderActivity.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel.component;
+
+import com.android.powermodel.ActivityReport;
+import com.android.powermodel.AttributionKey;
+import com.android.powermodel.Component;
+import com.android.powermodel.ComponentActivity;
+import com.android.powermodel.PowerProfile;
+import com.android.powermodel.util.Conversion;
+
+/**
+ * Encapsulates the work done by the remaining
+ */
+public class ModemRemainderActivity extends ComponentActivity {
+ /**
+ * Construct a new ModemRemainderActivity.
+ */
+ public ModemRemainderActivity(AttributionKey attribution) {
+ super(attribution);
+ }
+
+ /**
+ * Number of milliseconds spent at each of the signal strengths.
+ */
+ public long[] strengthTimeMs;
+
+ /**
+ * Number of milliseconds spent scanning for a network.
+ */
+ public long scanningTimeMs;
+
+ /**
+ * Number of milliseconds that the radio is active for reasons other
+ * than an app transmitting and receiving data.
+ */
+ public long activeTimeMs;
+
+ @Override
+ public ModemRemainderPower applyProfile(ActivityReport activityReport, PowerProfile profile) {
+ // Profile
+ final ModemProfile modemProfile = (ModemProfile)profile.getComponent(Component.MODEM);
+ if (modemProfile == null) {
+ return null;
+ }
+
+ // Activity
+ final ModemRemainderPower result = new ModemRemainderPower();
+ result.attribution = this.attribution;
+ result.activity = this;
+
+ // strengthMah
+ // TODO: If the array lengths don't match... then?
+ result.strengthMah = new double[this.strengthTimeMs.length];
+ for (int i=0; i<this.strengthTimeMs.length; i++) {
+ result.strengthMah[i] = Conversion.msToHr(
+ this.strengthTimeMs[i] * modemProfile.getTxMa()[i]);
+ result.powerMah += result.strengthMah[i];
+ }
+
+ // scanningMah
+ result.scanningMah = Conversion.msToHr(this.scanningTimeMs * modemProfile.getScanningMa());
+ result.powerMah += result.scanningMah;
+
+ // activeMah
+ result.activeMah = Conversion.msToHr(
+ this.activeTimeMs * ModemAppActivity.getAverageModemPowerMa(modemProfile));
+ result.powerMah += result.activeMah;
+
+ return result;
+ }
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/component/ModemRemainderPower.java b/tools/powermodel/src/com/android/powermodel/component/ModemRemainderPower.java
new file mode 100644
index 0000000..7f38cd3
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/component/ModemRemainderPower.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel.component;
+
+import com.android.powermodel.Component;
+import com.android.powermodel.ComponentPower;
+
+public class ModemRemainderPower extends ComponentPower<ModemRemainderActivity> {
+
+ public double[] strengthMah;
+
+ public double scanningMah;
+
+ public double activeMah;
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/component/ScreenProfile.java b/tools/powermodel/src/com/android/powermodel/component/ScreenProfile.java
new file mode 100644
index 0000000..e1051c6
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/component/ScreenProfile.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel.component;
+
+import java.util.Arrays;
+
+import com.android.powermodel.ComponentProfile;
+import com.android.powermodel.ParseException;
+
+public class ScreenProfile extends ComponentProfile {
+ public float onMa;
+ public float fullMa;
+ public float ambientMa;
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/component/VideoProfile.java b/tools/powermodel/src/com/android/powermodel/component/VideoProfile.java
new file mode 100644
index 0000000..5152795
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/component/VideoProfile.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel.component;
+
+import java.util.Arrays;
+
+import com.android.powermodel.ComponentProfile;
+import com.android.powermodel.ParseException;
+
+public class VideoProfile extends ComponentProfile {
+ public float onMa;
+}
+
+
diff --git a/tools/powermodel/src/com/android/powermodel/component/WifiProfile.java b/tools/powermodel/src/com/android/powermodel/component/WifiProfile.java
new file mode 100644
index 0000000..6f424bf
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/component/WifiProfile.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel.component;
+
+import java.util.Arrays;
+
+import com.android.powermodel.ComponentProfile;
+import com.android.powermodel.ParseException;
+
+public class WifiProfile extends ComponentProfile {
+ public float idleMa;
+ public float rxMa;
+ public float txMa;
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/util/Conversion.java b/tools/powermodel/src/com/android/powermodel/util/Conversion.java
new file mode 100644
index 0000000..e556c25
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/util/Conversion.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel.util;
+
+public class Conversion {
+
+ /**
+ * Convert the the float[] to an int[].
+ * <p>
+ * Values are rounded to the nearest integral value. Null input
+ * results in null output.
+ */
+ public static int[] toIntArray(float[] value) {
+ if (value == null) {
+ return null;
+ }
+ int[] result = new int[value.length];
+ for (int i=0; i<result.length; i++) {
+ result[i] = (int)(value[i] + 0.5f);
+ }
+ return result;
+ }
+
+ public static double msToHr(double ms) {
+ return ms / 3600.0 / 1000.0;
+ }
+
+ /**
+ * No public constructor.
+ */
+ private Conversion() {
+ }
+}
diff --git a/tools/powermodel/test-resource/bs.csv b/tools/powermodel/test-resource/bs.csv
new file mode 100644
index 0000000..6e84120
--- /dev/null
+++ b/tools/powermodel/test-resource/bs.csv
@@ -0,0 +1,7 @@
+9,0,i,vers,32,177,PPR1.180326.002,PQ1A.181105.015
+9,0,i,uid,10139,com.google.android.gm
+9,0,l,gn,108060756,17293456,4896592,3290614,97840,72941,6903,8107,390,105
+9,0,l,m,2590630,0,384554,3943868,5113727,265,2565483,0,16,0,0,0,0,192,25331,3472068,17,3543323,14,614050,0
+9,10139,l,nt,13688501,534571,13842,7792,9925,5577,30,67,190051799,27,0,0,5,3,126020,42343,13842,7792,207,167,30,67
+9,0,l,sgt,3066958,0,34678,1643364,7045084
+9,0,l,sst,2443805
diff --git a/tools/powermodel/test-resource/power_profile.xml b/tools/powermodel/test-resource/power_profile.xml
new file mode 100644
index 0000000..8e388ea
--- /dev/null
+++ b/tools/powermodel/test-resource/power_profile.xml
@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+
+<!-- Test power profile that parses correctly. -->
+<device>
+ <item name="battery.capacity">2915</item>
+
+ <!-- Number of cores each CPU cluster contains -->
+ <array name="cpu.clusters.cores">
+ <value>4</value>
+ <value>2</value>
+ </array>
+
+ <!-- Power consumption when CPU is suspended -->
+ <item name="cpu.suspend">1.3</item>
+
+ <!-- Additional power consumption when CPU is in a kernel idle loop -->
+ <item name="cpu.idle">3.9</item>
+
+ <!-- Additional power consumption by CPU excluding cluster and core when
+ running -->
+ <item name="cpu.active">18.33</item>
+
+ <!-- Additional power consumption by CPU cluster0 itself when running
+ excluding cores in it -->
+ <item name="cpu.cluster_power.cluster0">2.41</item>
+
+ <!-- Additional power consumption by CPU cluster1 itself when running
+ excluding cores in it -->
+ <item name="cpu.cluster_power.cluster1">5.29</item>
+
+ <!-- Different CPU speeds as reported in
+ /sys/devices/system/cpu/cpu0/cpufreq/stats/scaling_available_frequencies -->
+ <array name="cpu.core_speeds.cluster0">
+ <value>100000</value>
+ <value>303200</value>
+ <value>380000</value>
+ <value>476000</value>
+ <value>552800</value>
+ <value>648800</value>
+ <value>725600</value>
+ <value>802400</value>
+ <value>879200</value>
+ </array>
+
+ <!-- Different CPU speeds as reported in
+ /sys/devices/system/cpu/cpu4/cpufreq/stats/scaling_available_frequencies -->
+ <array name="cpu.core_speeds.cluster1">
+ <value>825600</value>
+ <value>902400</value>
+ <value>979200</value>
+ <value>1056000</value>
+ <value>1209600</value>
+ <value>1286400</value>
+ <value>1363200</value>
+ </array>
+
+ <!-- Additional power used by a CPU core from cluster 0 when running at
+ different speeds, excluding cluster and active cost -->
+ <array name="cpu.core_power.cluster0">
+ <value>0.29</value>
+ <value>0.63</value>
+ <value>1.23</value>
+ <value>1.24</value>
+ <value>2.47</value>
+ <value>2.54</value>
+ <value>3.60</value>
+ <value>3.64</value>
+ <value>4.42</value>
+ </array>
+
+ <!-- Additional power used by a CPU core from cluster 1 when running at
+ different speeds, excluding cluster and active cost -->
+ <array name="cpu.core_power.cluster1">
+ <value>28.98</value>
+ <value>31.40</value>
+ <value>33.33</value>
+ <value>40.12</value>
+ <value>44.10</value>
+ <value>90.14</value>
+ <value>100</value>
+ </array>
+
+ <!-- Additional power used when screen is ambient mode -->
+ <item name="ambient.on">12</item>
+
+ <!-- Additional power used when screen is turned on at minimum brightness -->
+ <item name="screen.on">102.4</item>
+ <!-- Additional power used when screen is at maximum brightness, compared to
+ screen at minimum brightness -->
+ <item name="screen.full">1234</item>
+
+ <!-- Average power used by the camera flash module when on -->
+ <item name="camera.flashlight">1233.47</item>
+
+ <!-- Average power use by the camera subsystem for a typical camera
+ application. Intended as a rough estimate for an application running a
+ preview and capturing approximately 10 full-resolution pictures per
+ minute. -->
+ <item name="camera.avg">941</item>
+
+ <!-- Additional power used when video is playing -->
+ <item name="video">123</item>
+
+ <!-- Additional power used when audio is playing -->
+ <item name="audio">12</item>
+
+ <!-- Cellular modem related values.-->
+ <item name="modem.controller.sleep">1</item>
+ <item name="modem.controller.idle">44</item>
+ <item name="modem.controller.rx">11</item>
+ <array name="modem.controller.tx"> <!-- Strength 0 to 4 -->
+ <value>16</value>
+ <value>19</value>
+ <value>22</value>
+ <value>73</value>
+ <value>132</value>
+ </array>
+ <item name="modem.controller.voltage">1400</item>
+ <item name="radio.scanning">12</item>
+
+ <!-- GPS related values.-->
+ <item name="gps.on">1</item>
+ <array name="gps.signalqualitybased"> <!-- Strength 0 to 1 -->
+ <value>88</value>
+ <value>07</value>
+ </array>
+ <item name="gps.voltage">1500</item>
+
+ <!-- Idle Receive current for wifi radio in mA.-->
+ <item name="wifi.controller.idle">2</item>
+
+ <!-- Rx current for wifi radio in mA.-->
+ <item name="wifi.controller.rx">123</item>
+
+ <!-- Tx current for wifi radio in mA-->
+ <item name="wifi.controller.tx">333</item>
+
+ <!-- Operating volatage for wifi radio in mV.-->
+ <item name="wifi.controller.voltage">3700</item>
+
+ <!-- Idle current for bluetooth in mA.-->
+ <item name="bluetooth.controller.idle">0.02</item>
+
+ <!-- Rx current for bluetooth in mA.-->
+ <item name="bluetooth.controller.rx">3</item>
+
+ <!-- Tx current for bluetooth in mA-->
+ <item name="bluetooth.controller.tx">5</item>
+
+ <!-- Operating voltage for bluetooth in mV.-->
+ <item name="bluetooth.controller.voltage">3300</item>
+
+</device>
+
+
diff --git a/tools/powermodel/test/com/android/powermodel/BatteryStatsReaderTest.java b/tools/powermodel/test/com/android/powermodel/BatteryStatsReaderTest.java
new file mode 100644
index 0000000..e7b2c37
--- /dev/null
+++ b/tools/powermodel/test/com/android/powermodel/BatteryStatsReaderTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import org.junit.Test;
+import org.junit.Assert;
+
+import com.android.powermodel.component.ModemAppActivity;
+import com.android.powermodel.component.ModemGlobalActivity;
+import com.android.powermodel.component.ModemRemainderActivity;
+
+/**
+ * Tests {@link BatteryStatsReader}.
+ */
+public class BatteryStatsReaderTest {
+ private static InputStream loadCsvStream() {
+ return BatteryStatsReaderTest.class.getResourceAsStream("/bs.csv");
+ }
+
+ @Test public void testModemGlobal() throws Exception {
+ final ActivityReport report = BatteryStatsReader.parse(loadCsvStream());
+
+ final AppActivity global = report.findApp(SpecialApp.GLOBAL);
+ Assert.assertNotNull(global);
+
+ final ModemGlobalActivity modem
+ = (ModemGlobalActivity)global.getComponentActivity(Component.MODEM);
+ Assert.assertNotNull(modem);
+ Assert.assertEquals(97840, modem.rxPacketCount);
+ Assert.assertEquals(72941, modem.txPacketCount);
+ Assert.assertEquals(5113727, modem.totalActiveTimeMs);
+ }
+
+ @Test public void testModemApp() throws Exception {
+ final ActivityReport report = BatteryStatsReader.parse(loadCsvStream());
+
+ final List<AppActivity> gmailList = report.findApp("com.google.android.gm");
+ Assert.assertEquals(1, gmailList.size());
+ final AppActivity gmail = gmailList.get(0);
+
+ final ModemAppActivity modem
+ = (ModemAppActivity)gmail.getComponentActivity(Component.MODEM);
+ Assert.assertNotNull(modem);
+ Assert.assertEquals(9925, modem.rxPacketCount);
+ Assert.assertEquals(5577, modem.txPacketCount);
+ }
+
+ @Test public void testModemRemainder() throws Exception {
+ final ActivityReport report = BatteryStatsReader.parse(loadCsvStream());
+
+ final AppActivity remainder = report.findApp(SpecialApp.REMAINDER);
+ Assert.assertNotNull(remainder);
+
+ final ModemRemainderActivity modem
+ = (ModemRemainderActivity)remainder.getComponentActivity(Component.MODEM);
+ Assert.assertNotNull(modem);
+ Assert.assertArrayEquals(new long[] { 3066958, 0, 34678, 1643364, 7045084 },
+ modem.strengthTimeMs);
+ Assert.assertEquals(2443805, modem.scanningTimeMs);
+ Assert.assertEquals(4923676, modem.activeTimeMs);
+ }
+}
diff --git a/tools/powermodel/test/com/android/powermodel/CsvParserTest.java b/tools/powermodel/test/com/android/powermodel/CsvParserTest.java
new file mode 100644
index 0000000..55dde41
--- /dev/null
+++ b/tools/powermodel/test/com/android/powermodel/CsvParserTest.java
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Tests {@link PowerProfile}
+ */
+public class CsvParserTest {
+
+ class LineCollector implements CsvParser.LineProcessor {
+ ArrayList<ArrayList<String>> results = new ArrayList<ArrayList<String>>();
+
+ @Override
+ public void onLine(int lineNumber, ArrayList<String> fields) {
+ System.out.println(lineNumber);
+ for (String str: fields) {
+ System.out.println("-->" + str + "<--");
+ }
+ results.add(fields);
+ }
+ }
+
+ private void assertEquals(String[][] expected, ArrayList<ArrayList<String>> results) {
+ final String[][] resultArray = new String[results.size()][];
+ for (int i=0; i<results.size(); i++) {
+ final ArrayList<String> list = results.get(i);
+ resultArray[i] = list.toArray(new String[list.size()]);
+ }
+ Assert.assertArrayEquals(expected, resultArray);
+ }
+
+ private String makeString(int length) {
+ final StringBuilder str = new StringBuilder();
+ for (int i=0; i<length; i++) {
+ str.append('a');
+ }
+ return str.toString();
+ }
+
+ @Test public void testEmpty() throws Exception {
+ final String text = "";
+ System.out.println("Test: [" + text + "]");
+ final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ LineCollector collector = new LineCollector();
+
+ CsvParser.parse(is, collector);
+
+ assertEquals(new String[][] {
+ }, collector.results);
+ }
+
+ @Test public void testOnlyNewline() throws Exception {
+ final String text = "\n";
+ System.out.println("Test: [" + text + "]");
+ final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ LineCollector collector = new LineCollector();
+
+ CsvParser.parse(is, collector);
+
+ assertEquals(new String[][] {
+ }, collector.results);
+ }
+
+ @Test public void testTwoLines() throws Exception {
+ final String text = "one,twoo,3\nfour,5,six\n";
+ System.out.println("Test: [" + text + "]");
+ final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ LineCollector collector = new LineCollector();
+
+ CsvParser.parse(is, collector);
+
+ assertEquals(new String[][] {
+ { "one", "twoo", "3", },
+ { "four", "5", "six", },
+ }, collector.results);
+ }
+
+
+ @Test public void testEscapedEmpty() throws Exception {
+ final String text = "\"\",\"\",\"\"\n";
+ System.out.println("Test: [" + text + "]");
+ final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ LineCollector collector = new LineCollector();
+
+ CsvParser.parse(is, collector);
+
+ assertEquals(new String[][] {
+ { "", "", "", },
+ }, collector.results);
+ }
+
+ @Test public void testEscapedText() throws Exception {
+ final String text = "\"one\",\"twoo\",\"3\"\n";
+ System.out.println("Test: [" + text + "]");
+ final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ LineCollector collector = new LineCollector();
+
+ CsvParser.parse(is, collector);
+
+ assertEquals(new String[][] {
+ { "one", "twoo", "3", },
+ }, collector.results);
+ }
+
+ @Test public void testEscapedQuotes() throws Exception {
+ final String text = "\"\"\"\",\"\"\"\"\"\",\"\"\"\"\n";
+ System.out.println("Test: [" + text + "]");
+ final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ LineCollector collector = new LineCollector();
+
+ CsvParser.parse(is, collector);
+
+ assertEquals(new String[][] {
+ { "\"", "\"\"", "\"", },
+ }, collector.results);
+ }
+
+ @Test public void testEscapedCommas() throws Exception {
+ final String text = "\",\",\",\",\",\"\n";
+ System.out.println("Test: [" + text + "]");
+ final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ LineCollector collector = new LineCollector();
+
+ CsvParser.parse(is, collector);
+
+ assertEquals(new String[][] {
+ { ",", ",", ",", },
+ }, collector.results);
+ }
+
+ @Test public void testEscapedQuotesAndCommas() throws Exception {
+ final String text = "\"\"\",\",\"\"\",\",\"\"\",\"\n";
+ System.out.println("Test: [" + text + "]");
+ final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ LineCollector collector = new LineCollector();
+
+ CsvParser.parse(is, collector);
+
+ assertEquals(new String[][] {
+ { "\",", "\",", "\",", },
+ }, collector.results);
+ }
+
+ @Test public void testNoNewline() throws Exception {
+ final String text = "a,b,c";
+ System.out.println("Test: [" + text + "]");
+ final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ LineCollector collector = new LineCollector();
+
+ CsvParser.parse(is, collector);
+
+ assertEquals(new String[][] {
+ { "a", "b", "c", }
+ }, collector.results);
+ }
+
+ @Test public void testNoNewlineWithCommas() throws Exception {
+ final String text = "a,b,,";
+ System.out.println("Test: [" + text + "]");
+ final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ LineCollector collector = new LineCollector();
+
+ CsvParser.parse(is, collector);
+
+ assertEquals(new String[][] {
+ { "a", "b", "", "" }
+ }, collector.results);
+ }
+
+ @Test public void testNoNewlineWithQuote() throws Exception {
+ final String text = "a,b,\",\"";
+ System.out.println("Test: [" + text + "]");
+ final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ LineCollector collector = new LineCollector();
+
+ CsvParser.parse(is, collector);
+
+ assertEquals(new String[][] {
+ { "a", "b", "," }
+ }, collector.results);
+ }
+
+ @Test public void testNoCommas() throws Exception {
+ final String text = "aasdfadfadfad";
+ System.out.println("Test: [" + text + "]");
+ final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ LineCollector collector = new LineCollector();
+
+ CsvParser.parse(is, collector);
+
+ assertEquals(new String[][] {
+ { "aasdfadfadfad", }
+ }, collector.results);
+ }
+
+ @Test public void testMaxLength() throws Exception {
+ final String text = makeString(CsvParser.MAX_FIELD_SIZE);
+ System.out.println("Test: [" + text + "]");
+ final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ LineCollector collector = new LineCollector();
+
+ CsvParser.parse(is, collector);
+
+ assertEquals(new String[][] {
+ { text, }
+ }, collector.results);
+ }
+
+ @Test public void testMaxLengthTwice() throws Exception {
+ String big = makeString(CsvParser.MAX_FIELD_SIZE);
+ final String text = big + "," + big;
+ System.out.println("Test: [" + text + "]");
+ final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ LineCollector collector = new LineCollector();
+
+ CsvParser.parse(is, collector);
+
+ assertEquals(new String[][] {
+ { big, big, }
+ }, collector.results);
+ }
+
+ @Test public void testTooLong() throws Exception {
+ final String text = makeString(CsvParser.MAX_FIELD_SIZE+1);
+ final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ LineCollector collector = new LineCollector();
+
+ try {
+ CsvParser.parse(is, collector);
+ throw new RuntimeException("Expected CsvParser.parse to throw ParseException");
+ } catch (ParseException ex) {
+ // good
+ }
+ }
+
+ @Test public void testBufferBoundary() throws Exception {
+ final String big = makeString(CsvParser.MAX_FIELD_SIZE-3);
+ final String text = big + ",b,c,d,e,f,g";
+ final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ LineCollector collector = new LineCollector();
+
+ CsvParser.parse(is, collector);
+
+ assertEquals(new String[][] {
+ { big, "b", "c", "d", "e", "f", "g", }
+ }, collector.results);
+ }
+
+ @Test public void testBufferBoundaryEmpty() throws Exception {
+ final String big = makeString(CsvParser.MAX_FIELD_SIZE-3);
+ final String text = big + ",,,,,,";
+ final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ LineCollector collector = new LineCollector();
+
+ CsvParser.parse(is, collector);
+
+ assertEquals(new String[][] {
+ { big, "", "", "", "", "", "", }
+ }, collector.results);
+ }
+
+ // Checks that the escaping and sawQuote behavior is correct at the buffer boundary
+ @Test public void testBufferBoundaryEscapingEven() throws Exception {
+ final String big = makeString(CsvParser.MAX_FIELD_SIZE-2);
+ final String text = big + ",\"\"\"\"\"\"\"\"\"\"\"\"," + big;
+ final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ LineCollector collector = new LineCollector();
+
+ CsvParser.parse(is, collector);
+
+ assertEquals(new String[][] {
+ { big, "\"\"\"\"\"", big }
+ }, collector.results);
+ }
+
+ // Checks that the escaping and sawQuote behavior is correct at the buffer boundary
+ @Test public void testBufferBoundaryEscapingOdd() throws Exception {
+ final String big = makeString(CsvParser.MAX_FIELD_SIZE-3);
+ final String text = big + ",\"\"\"\"\"\"\"\"\"\"\"\"," + big;
+ final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ LineCollector collector = new LineCollector();
+
+ CsvParser.parse(is, collector);
+
+ assertEquals(new String[][] {
+ { big, "\"\"\"\"\"", big }
+ }, collector.results);
+ }
+
+}
diff --git a/tools/powermodel/test/com/android/powermodel/PowerProfileTest.java b/tools/powermodel/test/com/android/powermodel/PowerProfileTest.java
new file mode 100644
index 0000000..ab45831
--- /dev/null
+++ b/tools/powermodel/test/com/android/powermodel/PowerProfileTest.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel;
+
+import java.io.InputStream;
+
+import com.android.powermodel.component.CpuProfile;
+import com.android.powermodel.component.AudioProfile;
+import com.android.powermodel.component.BluetoothProfile;
+import com.android.powermodel.component.CameraProfile;
+import com.android.powermodel.component.FlashlightProfile;
+import com.android.powermodel.component.GpsProfile;
+import com.android.powermodel.component.ModemProfile;
+import com.android.powermodel.component.ScreenProfile;
+import com.android.powermodel.component.VideoProfile;
+import com.android.powermodel.component.WifiProfile;
+import org.junit.Assert;
+import org.junit.Test;
+
+/*
+ * Additional tests needed:
+ * - CPU clusters with mismatching counts of speeds and coefficients
+ * - Extra fields
+ * - Name listed twice
+ */
+
+/**
+ * Tests {@link PowerProfile}
+ */
+public class PowerProfileTest {
+ private static final float EPSILON = 0.00001f;
+
+ private static InputStream loadPowerProfileStream() {
+ return PowerProfileTest.class.getResourceAsStream("/power_profile.xml");
+ }
+
+ @Test public void testReadGood() throws Exception {
+ final InputStream is = loadPowerProfileStream();
+
+ final PowerProfile profile = PowerProfile.parse(is);
+
+ // Audio
+ final AudioProfile audio = (AudioProfile)profile.getComponent(Component.AUDIO);
+ Assert.assertEquals(12.0f, audio.onMa, EPSILON);
+
+ // Bluetooth
+ final BluetoothProfile bluetooth
+ = (BluetoothProfile)profile.getComponent(Component.BLUETOOTH);
+ Assert.assertEquals(0.02f, bluetooth.idleMa, EPSILON);
+ Assert.assertEquals(3.0f, bluetooth.rxMa, EPSILON);
+ Assert.assertEquals(5.0f, bluetooth.txMa, EPSILON);
+
+ // Camera
+ final CameraProfile camera = (CameraProfile)profile.getComponent(Component.CAMERA);
+ Assert.assertEquals(941.0f, camera.onMa, EPSILON);
+
+ // CPU
+ final CpuProfile cpu = (CpuProfile)profile.getComponent(Component.CPU);
+ Assert.assertEquals(1.3f, cpu.suspendMa, EPSILON);
+ Assert.assertEquals(3.9f, cpu.idleMa, EPSILON);
+ Assert.assertEquals(18.33f, cpu.activeMa, EPSILON);
+ Assert.assertEquals(2, cpu.clusters.length);
+ // Cluster 0
+ Assert.assertEquals(4, cpu.clusters[0].coreCount);
+ Assert.assertEquals(2.41f, cpu.clusters[0].onMa, EPSILON);
+ Assert.assertEquals(9, cpu.clusters[0].frequencies.length, EPSILON);
+ Assert.assertEquals(100000, cpu.clusters[0].frequencies[0].speedHz);
+ Assert.assertEquals(0.29f, cpu.clusters[0].frequencies[0].onMa, EPSILON);
+ Assert.assertEquals(303200, cpu.clusters[0].frequencies[1].speedHz);
+ Assert.assertEquals(0.63f, cpu.clusters[0].frequencies[1].onMa, EPSILON);
+ Assert.assertEquals(380000, cpu.clusters[0].frequencies[2].speedHz);
+ Assert.assertEquals(1.23f, cpu.clusters[0].frequencies[2].onMa, EPSILON);
+ Assert.assertEquals(476000, cpu.clusters[0].frequencies[3].speedHz);
+ Assert.assertEquals(1.24f, cpu.clusters[0].frequencies[3].onMa, EPSILON);
+ Assert.assertEquals(552800, cpu.clusters[0].frequencies[4].speedHz);
+ Assert.assertEquals(2.47f, cpu.clusters[0].frequencies[4].onMa, EPSILON);
+ Assert.assertEquals(648800, cpu.clusters[0].frequencies[5].speedHz);
+ Assert.assertEquals(2.54f, cpu.clusters[0].frequencies[5].onMa, EPSILON);
+ Assert.assertEquals(725600, cpu.clusters[0].frequencies[6].speedHz);
+ Assert.assertEquals(3.60f, cpu.clusters[0].frequencies[6].onMa, EPSILON);
+ Assert.assertEquals(802400, cpu.clusters[0].frequencies[7].speedHz);
+ Assert.assertEquals(3.64f, cpu.clusters[0].frequencies[7].onMa, EPSILON);
+ Assert.assertEquals(879200, cpu.clusters[0].frequencies[8].speedHz);
+ Assert.assertEquals(4.42f, cpu.clusters[0].frequencies[8].onMa, EPSILON);
+ // Cluster 1
+ Assert.assertEquals(2, cpu.clusters[1].coreCount);
+ Assert.assertEquals(5.29f, cpu.clusters[1].onMa, EPSILON);
+ Assert.assertEquals(7, cpu.clusters[1].frequencies.length, EPSILON);
+ Assert.assertEquals(825600, cpu.clusters[1].frequencies[0].speedHz);
+ Assert.assertEquals(28.98f, cpu.clusters[1].frequencies[0].onMa, EPSILON);
+ Assert.assertEquals(902400, cpu.clusters[1].frequencies[1].speedHz);
+ Assert.assertEquals(31.40f, cpu.clusters[1].frequencies[1].onMa, EPSILON);
+ Assert.assertEquals(979200, cpu.clusters[1].frequencies[2].speedHz);
+ Assert.assertEquals(33.33f, cpu.clusters[1].frequencies[2].onMa, EPSILON);
+ Assert.assertEquals(1056000, cpu.clusters[1].frequencies[3].speedHz);
+ Assert.assertEquals(40.12f, cpu.clusters[1].frequencies[3].onMa, EPSILON);
+ Assert.assertEquals(1209600, cpu.clusters[1].frequencies[4].speedHz);
+ Assert.assertEquals(44.10f, cpu.clusters[1].frequencies[4].onMa, EPSILON);
+ Assert.assertEquals(1286400, cpu.clusters[1].frequencies[5].speedHz);
+ Assert.assertEquals(90.14f, cpu.clusters[1].frequencies[5].onMa, EPSILON);
+ Assert.assertEquals(1363200, cpu.clusters[1].frequencies[6].speedHz);
+ Assert.assertEquals(100f, cpu.clusters[1].frequencies[6].onMa, EPSILON);
+
+ // Flashlight
+ final FlashlightProfile flashlight
+ = (FlashlightProfile)profile.getComponent(Component.FLASHLIGHT);
+ Assert.assertEquals(1233.47f, flashlight.onMa, EPSILON);
+
+ // GPS
+ final GpsProfile gps = (GpsProfile)profile.getComponent(Component.GPS);
+ Assert.assertEquals(1.0f, gps.onMa, EPSILON);
+ Assert.assertEquals(2, gps.signalQualityMa.length);
+ Assert.assertEquals(88.0f, gps.signalQualityMa[0], EPSILON);
+ Assert.assertEquals(7.0f, gps.signalQualityMa[1], EPSILON);
+
+ // Modem
+ final ModemProfile modem = (ModemProfile)profile.getComponent(Component.MODEM);
+ Assert.assertEquals(1.0f, modem.sleepMa, EPSILON);
+ Assert.assertEquals(44.0f, modem.idleMa, EPSILON);
+ Assert.assertEquals(12.0f, modem.scanningMa, EPSILON);
+ Assert.assertEquals(11.0f, modem.rxMa, EPSILON);
+ Assert.assertEquals(5, modem.txMa.length);
+ Assert.assertEquals(16.0f, modem.txMa[0], EPSILON);
+ Assert.assertEquals(19.0f, modem.txMa[1], EPSILON);
+ Assert.assertEquals(22.0f, modem.txMa[2], EPSILON);
+ Assert.assertEquals(73.0f, modem.txMa[3], EPSILON);
+ Assert.assertEquals(132.0f, modem.txMa[4], EPSILON);
+
+ // Screen
+ final ScreenProfile screen = (ScreenProfile)profile.getComponent(Component.SCREEN);
+ Assert.assertEquals(102.4f, screen.onMa, EPSILON);
+ Assert.assertEquals(1234.0f, screen.fullMa, EPSILON);
+ Assert.assertEquals(12.0f, screen.ambientMa, EPSILON);
+
+ // Video
+ final VideoProfile video = (VideoProfile)profile.getComponent(Component.VIDEO);
+ Assert.assertEquals(123.0f, video.onMa, EPSILON);
+
+ // Wifi
+ final WifiProfile wifi = (WifiProfile)profile.getComponent(Component.WIFI);
+ Assert.assertEquals(2.0f, wifi.idleMa, EPSILON);
+ Assert.assertEquals(123.0f, wifi.rxMa, EPSILON);
+ Assert.assertEquals(333.0f, wifi.txMa, EPSILON);
+ }
+}
diff --git a/tools/powermodel/test/com/android/powermodel/PowerReportTest.java b/tools/powermodel/test/com/android/powermodel/PowerReportTest.java
new file mode 100644
index 0000000..1a61737
--- /dev/null
+++ b/tools/powermodel/test/com/android/powermodel/PowerReportTest.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import org.junit.Test;
+import org.junit.Assert;
+
+import com.android.powermodel.component.ModemAppPower;
+import com.android.powermodel.component.ModemRemainderPower;
+
+/**
+ * Tests {@link PowerReport}.
+ */
+public class PowerReportTest {
+ private static final double EPSILON = 0.001;
+ private static final double MS_PER_HR = 3600000.0;
+
+ private static final double AVERAGE_MODEM_POWER = ((11+16+19+22+73+132) / 6.0);
+ private static final double GMAIL_MODEM_MAH = ((9925+5577) / (double)(97840+72941))
+ * 5113727 * AVERAGE_MODEM_POWER * (1.0 / 3600 / 1000);
+ private static final double GMAIL_MAH
+ = GMAIL_MODEM_MAH;
+
+ private static final double REMAINDER_MODEM_MAH
+ = (1.0 / 3600 / 1000)
+ * ((3066958 * 16) + (0 * 19) + (34678 * 22) + (1643364 * 73) + (7045084 * 132)
+ + (2443805 * 12)
+ + (4923676 * AVERAGE_MODEM_POWER));
+ private static final double REMAINDER_MAH
+ = REMAINDER_MODEM_MAH;
+
+ private static final double TOTAL_MAH
+ = GMAIL_MAH
+ + REMAINDER_MAH;
+
+ private static InputStream loadPowerProfileStream() {
+ return PowerProfileTest.class.getResourceAsStream("/power_profile.xml");
+ }
+
+ private static InputStream loadCsvStream() {
+ return BatteryStatsReaderTest.class.getResourceAsStream("/bs.csv");
+ }
+
+ private static PowerReport loadPowerReport() throws Exception {
+ final PowerProfile profile = PowerProfile.parse(loadPowerProfileStream());
+ final ActivityReport activity = BatteryStatsReader.parse(loadCsvStream());
+ return PowerReport.createReport(profile, activity);
+ }
+
+ @Test public void testModemApp() throws Exception {
+ final PowerReport report = loadPowerReport();
+
+ final List<AppPower> gmailList = report.findApp("com.google.android.gm");
+ Assert.assertEquals(1, gmailList.size());
+ final AppPower gmail = gmailList.get(0);
+
+ final ModemAppPower modem = (ModemAppPower)gmail.getComponentPower(Component.MODEM);
+ Assert.assertNotNull(modem);
+ Assert.assertEquals(GMAIL_MODEM_MAH, modem.powerMah, EPSILON);
+ }
+
+ @Test public void testModemRemainder() throws Exception {
+ final PowerReport report = loadPowerReport();
+
+ final AppPower remainder = report.findApp(SpecialApp.REMAINDER);
+ Assert.assertNotNull(remainder);
+
+ final ModemRemainderPower modem
+ = (ModemRemainderPower)remainder.getComponentPower(Component.MODEM);
+ Assert.assertNotNull(modem);
+
+ Assert.assertArrayEquals(new double[] {
+ 3066958 * 16.0 / MS_PER_HR,
+ 0 * 19.0 / MS_PER_HR,
+ 34678 * 22.0 / MS_PER_HR,
+ 1643364 * 73.0 / MS_PER_HR,
+ 7045084 * 132.0 / MS_PER_HR },
+ modem.strengthMah, EPSILON);
+ Assert.assertEquals(2443805 * 12 / MS_PER_HR, modem.scanningMah, EPSILON);
+ Assert.assertEquals(4923676 * AVERAGE_MODEM_POWER / MS_PER_HR, modem.activeMah, EPSILON);
+
+ Assert.assertEquals(REMAINDER_MODEM_MAH, modem.powerMah, EPSILON);
+ }
+
+ @Test public void testAppTotal() throws Exception {
+ final PowerReport report = loadPowerReport();
+
+ final List<AppPower> gmailList = report.findApp("com.google.android.gm");
+ Assert.assertEquals(1, gmailList.size());
+ final AppPower gmail = gmailList.get(0);
+
+ Assert.assertEquals(GMAIL_MAH, gmail.getAppPowerMah(), EPSILON);
+ }
+
+ @Test public void testRemainderTotal() throws Exception {
+ final PowerReport report = loadPowerReport();
+
+ final AppPower remainder = report.findApp(SpecialApp.REMAINDER);
+ Assert.assertNotNull(remainder);
+
+ Assert.assertEquals(REMAINDER_MAH, remainder.getAppPowerMah(), EPSILON);
+ }
+
+ @Test public void testTotal() throws Exception {
+ final PowerReport report = loadPowerReport();
+
+ Assert.assertEquals(TOTAL_MAH, report.getTotalPowerMah(), EPSILON);
+ }
+}
+
diff --git a/tools/powermodel/test/com/android/powermodel/RawBatteryStatsTest.java b/tools/powermodel/test/com/android/powermodel/RawBatteryStatsTest.java
new file mode 100644
index 0000000..fbcac41
--- /dev/null
+++ b/tools/powermodel/test/com/android/powermodel/RawBatteryStatsTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import org.junit.Test;
+import org.junit.Assert;
+
+/**
+ * Tests {@link RawBatteryStats}.
+ */
+public class RawBatteryStatsTest {
+ private static final int BS_VERSION = 32;
+
+ private static InputStream makeCsv(String... lines) {
+ return makeCsv(BS_VERSION, lines);
+ }
+
+ private static InputStream makeCsv(int version, String... lines) {
+ final StringBuilder result = new StringBuilder("9,0,i,vers,");
+ result.append(version);
+ result.append(",177,PPR1.180326.002,PQ1A.181105.015\n");
+ for (String line: lines) {
+ result.append(line);
+ result.append('\n');
+ }
+ return new ByteArrayInputStream(result.toString().getBytes(StandardCharsets.UTF_8));
+ }
+
+ @Test public void testVersion() throws Exception {
+ final InputStream is = makeCsv();
+
+ final RawBatteryStats bs = RawBatteryStats.parse(is);
+ final List<RawBatteryStats.Record> records = bs.getRecords();
+ final RawBatteryStats.Version line = (RawBatteryStats.Version)records.get(0);
+
+ Assert.assertEquals(0, bs.getWarnings().size());
+ Assert.assertEquals(true, line.complete);
+
+ Assert.assertEquals(9, line.lineVersion);
+ Assert.assertEquals(0, line.uid);
+ Assert.assertEquals(RawBatteryStats.Category.INFO, line.category);
+ Assert.assertEquals("vers", line.lineType);
+
+ Assert.assertEquals(BS_VERSION, line.dumpsysVersion);
+ Assert.assertEquals(177, line.parcelVersion);
+ Assert.assertEquals("PPR1.180326.002", line.startPlatformVersion);
+ Assert.assertEquals("PQ1A.181105.015", line.endPlatformVersion);
+ }
+
+ @Test public void testUid() throws Exception {
+ final InputStream is = makeCsv("9,0,i,uid,1000,com.example.app");
+
+ final RawBatteryStats bs = RawBatteryStats.parse(is);
+ final List<RawBatteryStats.Record> records = bs.getRecords();
+ final RawBatteryStats.Uid line = (RawBatteryStats.Uid)records.get(1);
+
+ Assert.assertEquals(1000, line.uidKey);
+ Assert.assertEquals("com.example.app", line.pkg);
+ }
+
+ @Test public void testVarargs() throws Exception {
+ final InputStream is = makeCsv("9,0,i,gmcd,1,2,3,4,5,6,7");
+
+ final RawBatteryStats bs = RawBatteryStats.parse(is);
+ final List<RawBatteryStats.Record> records = bs.getRecords();
+ final RawBatteryStats.GlobalModemController line
+ = (RawBatteryStats.GlobalModemController)records.get(1);
+
+ Assert.assertEquals(1, line.idleMs);
+ Assert.assertEquals(2, line.rxTimeMs);
+ Assert.assertEquals(3, line.powerMaMs);
+ Assert.assertEquals(4, line.txTimeMs.length);
+ Assert.assertEquals(4, line.txTimeMs[0]);
+ Assert.assertEquals(5, line.txTimeMs[1]);
+ Assert.assertEquals(6, line.txTimeMs[2]);
+ Assert.assertEquals(7, line.txTimeMs[3]);
+ }
+}
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
index 8585ae9..88b7e2e 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -1128,7 +1128,10 @@
hadStringOrChain = true;
fprintf(out, " jbyte* jbyte_array%d;\n", argIndex);
fprintf(out, " const char* str%d;\n", argIndex);
- fprintf(out, " if (arg%d != NULL) {\n", argIndex);
+ fprintf(out,
+ " if (arg%d != NULL && env->GetArrayLength(arg%d) > "
+ "0) {\n",
+ argIndex, argIndex);
fprintf(out,
" jbyte_array%d = "
"env->GetByteArrayElements(arg%d, NULL);\n",
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 3ec8a41..364d5084 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -58,7 +58,7 @@
*/
oneway void requestActivityInfo(in ResultReceiver result);
- ParceledListSlice getConfiguredNetworks();
+ ParceledListSlice getConfiguredNetworks(String packageName);
ParceledListSlice getPrivilegedConfiguredNetworks();
@@ -90,11 +90,11 @@
List<ScanResult> getScanResults(String callingPackage);
- void disconnect(String packageName);
+ boolean disconnect(String packageName);
- void reconnect(String packageName);
+ boolean reconnect(String packageName);
- void reassociate(String packageName);
+ boolean reassociate(String packageName);
WifiInfo getConnectionInfo(String callingPackage);
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 7aff03c..8dd6c77 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1067,7 +1067,7 @@
public List<WifiConfiguration> getConfiguredNetworks() {
try {
ParceledListSlice<WifiConfiguration> parceledList =
- mService.getConfiguredNetworks();
+ mService.getConfiguredNetworks(mContext.getOpPackageName());
if (parceledList == null) {
return Collections.emptyList();
}
@@ -1761,8 +1761,7 @@
@Deprecated
public boolean disconnect() {
try {
- mService.disconnect(mContext.getOpPackageName());
- return true;
+ return mService.disconnect(mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1786,8 +1785,7 @@
@Deprecated
public boolean reconnect() {
try {
- mService.reconnect(mContext.getOpPackageName());
- return true;
+ return mService.reconnect(mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1811,8 +1809,7 @@
@Deprecated
public boolean reassociate() {
try {
- mService.reassociate(mContext.getOpPackageName());
- return true;
+ return mService.reassociate(mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2132,14 +2129,14 @@
* existing networks. You should assume the network IDs can be different
* after calling this method.
*
- * @return {@code false} Will always return true.
+ * @return {@code false}.
* @deprecated There is no need to call this method -
* {@link #addNetwork(WifiConfiguration)}, {@link #updateNetwork(WifiConfiguration)}
* and {@link #removeNetwork(int)} already persist the configurations automatically.
*/
@Deprecated
public boolean saveConfiguration() {
- return true;
+ return false;
}
/**
@@ -3406,6 +3403,11 @@
* @hide
*/
@SystemApi
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_SETUP_WIZARD,
+ android.Manifest.permission.NETWORK_STACK
+ })
public void connect(WifiConfiguration config, ActionListener listener) {
if (config == null) throw new IllegalArgumentException("config cannot be null");
// Use INVALID_NETWORK_ID for arg1 when passing a config object
@@ -3426,7 +3428,12 @@
* initialized again
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_SETUP_WIZARD,
+ android.Manifest.permission.NETWORK_STACK
+ })
public void connect(int networkId, ActionListener listener) {
if (networkId < 0) throw new IllegalArgumentException("Network id cannot be negative");
getChannel().sendMessage(CONNECT_NETWORK, networkId, putListener(listener));
@@ -3452,7 +3459,12 @@
* initialized again
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_SETUP_WIZARD,
+ android.Manifest.permission.NETWORK_STACK
+ })
public void save(WifiConfiguration config, ActionListener listener) {
if (config == null) throw new IllegalArgumentException("config cannot be null");
getChannel().sendMessage(SAVE_NETWORK, 0, putListener(listener), config);
@@ -3471,7 +3483,12 @@
* initialized again
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_SETUP_WIZARD,
+ android.Manifest.permission.NETWORK_STACK
+ })
public void forget(int netId, ActionListener listener) {
if (netId < 0) throw new IllegalArgumentException("Network id cannot be negative");
getChannel().sendMessage(FORGET_NETWORK, netId, putListener(listener));
@@ -3486,7 +3503,12 @@
* initialized again
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_SETUP_WIZARD,
+ android.Manifest.permission.NETWORK_STACK
+ })
public void disable(int netId, ActionListener listener) {
if (netId < 0) throw new IllegalArgumentException("Network id cannot be negative");
getChannel().sendMessage(DISABLE_NETWORK, netId, putListener(listener));
@@ -3498,6 +3520,12 @@
* @param SSID, in the format of WifiConfiguration's SSID.
* @hide
*/
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_SETUP_WIZARD,
+ android.Manifest.permission.NETWORK_STACK
+ })
public void disableEphemeralNetwork(String SSID) {
if (SSID == null) throw new IllegalArgumentException("SSID cannot be null");
try {
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index 529548f..6622a25 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -184,6 +184,9 @@
public static final String SCAN_PARAMS_SCAN_SETTINGS_KEY = "ScanSettings";
/** {@hide} */
public static final String SCAN_PARAMS_WORK_SOURCE_KEY = "WorkSource";
+ /** {@hide} */
+ public static final String REQUEST_PACKAGE_NAME_KEY = "PackageName";
+
/**
* scan configuration parameters to be sent to {@link #startBackgroundScan}
*/
@@ -798,6 +801,7 @@
Bundle scanParams = new Bundle();
scanParams.putParcelable(SCAN_PARAMS_SCAN_SETTINGS_KEY, settings);
scanParams.putParcelable(SCAN_PARAMS_WORK_SOURCE_KEY, workSource);
+ scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName());
mAsyncChannel.sendMessage(CMD_START_BACKGROUND_SCAN, 0, key, scanParams);
}
@@ -812,8 +816,11 @@
int key = removeListener(listener);
if (key == INVALID_KEY) return;
validateChannel();
- mAsyncChannel.sendMessage(CMD_STOP_BACKGROUND_SCAN, 0, key);
+ Bundle scanParams = new Bundle();
+ scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName());
+ mAsyncChannel.sendMessage(CMD_STOP_BACKGROUND_SCAN, 0, key, scanParams);
}
+
/**
* reports currently available scan results on appropriate listeners
* @return true if all scan results were reported correctly
@@ -821,7 +828,10 @@
@RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
public boolean getScanResults() {
validateChannel();
- Message reply = mAsyncChannel.sendMessageSynchronously(CMD_GET_SCAN_RESULTS, 0);
+ Bundle scanParams = new Bundle();
+ scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName());
+ Message reply =
+ mAsyncChannel.sendMessageSynchronously(CMD_GET_SCAN_RESULTS, 0, 0, scanParams);
return reply.what == CMD_OP_SUCCEEDED;
}
@@ -856,6 +866,7 @@
Bundle scanParams = new Bundle();
scanParams.putParcelable(SCAN_PARAMS_SCAN_SETTINGS_KEY, settings);
scanParams.putParcelable(SCAN_PARAMS_WORK_SOURCE_KEY, workSource);
+ scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName());
mAsyncChannel.sendMessage(CMD_START_SINGLE_SCAN, 0, key, scanParams);
}
@@ -870,7 +881,9 @@
int key = removeListener(listener);
if (key == INVALID_KEY) return;
validateChannel();
- mAsyncChannel.sendMessage(CMD_STOP_SINGLE_SCAN, 0, key);
+ Bundle scanParams = new Bundle();
+ scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName());
+ mAsyncChannel.sendMessage(CMD_STOP_SINGLE_SCAN, 0, key, scanParams);
}
/**
@@ -879,7 +892,10 @@
*/
public List<ScanResult> getSingleScanResults() {
validateChannel();
- Message reply = mAsyncChannel.sendMessageSynchronously(CMD_GET_SINGLE_SCAN_RESULTS, 0);
+ Bundle scanParams = new Bundle();
+ scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName());
+ Message reply = mAsyncChannel.sendMessageSynchronously(CMD_GET_SINGLE_SCAN_RESULTS, 0, 0,
+ scanParams);
if (reply.what == WifiScanner.CMD_OP_SUCCEEDED) {
return Arrays.asList(((ParcelableScanResults) reply.obj).getResults());
}
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
index 6772096..6631fa8 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
@@ -16,10 +16,16 @@
package android.net.wifi.p2p;
+import android.annotation.IntDef;
import android.annotation.UnsupportedAppUsage;
+import android.net.MacAddress;
import android.net.wifi.WpsInfo;
-import android.os.Parcelable;
import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
/**
* A class representing a Wi-Fi P2p configuration for setting up a connection
@@ -38,12 +44,46 @@
*/
public WpsInfo wps;
+ /**
+ * The network name of a group, should be configured by helper method
+ */
+ /** @hide */
+ public String networkName = "";
+
+ /**
+ * The passphrase of a group, should be configured by helper method
+ */
+ /** @hide */
+ public String passphrase = "";
+
+ /**
+ * The required band for Group Owner
+ */
+ /** @hide */
+ public int groupOwnerBand = GROUP_OWNER_BAND_AUTO;
+
/** @hide */
public static final int MAX_GROUP_OWNER_INTENT = 15;
/** @hide */
@UnsupportedAppUsage
public static final int MIN_GROUP_OWNER_INTENT = 0;
+ /** @hide */
+ @IntDef(flag = false, prefix = { "GROUP_OWNER_BAND_" }, value = {
+ GROUP_OWNER_BAND_AUTO,
+ GROUP_OWNER_BAND_2GHZ,
+ GROUP_OWNER_BAND_5GHZ
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface GroupOwnerBandType {}
+
+ /**
+ * Recognized Group Owner required band.
+ */
+ public static final int GROUP_OWNER_BAND_AUTO = 0;
+ public static final int GROUP_OWNER_BAND_2GHZ = 1;
+ public static final int GROUP_OWNER_BAND_5GHZ = 2;
+
/**
* This is an integer value between 0 and 15 where 0 indicates the least
* inclination to be a group owner and 15 indicates the highest inclination
@@ -115,6 +155,10 @@
sbuf.append("\n wps: ").append(wps);
sbuf.append("\n groupOwnerIntent: ").append(groupOwnerIntent);
sbuf.append("\n persist: ").append(netId);
+ sbuf.append("\n networkName: ").append(networkName);
+ sbuf.append("\n passphrase: ").append(
+ TextUtils.isEmpty(passphrase) ? "<empty>" : "<non-empty>");
+ sbuf.append("\n groupOwnerBand: ").append(groupOwnerBand);
return sbuf.toString();
}
@@ -130,6 +174,9 @@
wps = new WpsInfo(source.wps);
groupOwnerIntent = source.groupOwnerIntent;
netId = source.netId;
+ networkName = source.networkName;
+ passphrase = source.passphrase;
+ groupOwnerBand = source.groupOwnerBand;
}
}
@@ -139,6 +186,9 @@
dest.writeParcelable(wps, flags);
dest.writeInt(groupOwnerIntent);
dest.writeInt(netId);
+ dest.writeString(networkName);
+ dest.writeString(passphrase);
+ dest.writeInt(groupOwnerBand);
}
/** Implement the Parcelable interface */
@@ -150,6 +200,9 @@
config.wps = (WpsInfo) in.readParcelable(null);
config.groupOwnerIntent = in.readInt();
config.netId = in.readInt();
+ config.networkName = in.readString();
+ config.passphrase = in.readString();
+ config.groupOwnerBand = in.readInt();
return config;
}
@@ -157,4 +210,140 @@
return new WifiP2pConfig[size];
}
};
+
+ /**
+ * Builder used to build {@link WifiP2pConfig} objects for
+ * creating or joining a group.
+ */
+ public static final class Builder {
+
+ private static final MacAddress MAC_ANY_ADDRESS =
+ MacAddress.fromString("00:00:00:00:00:00");
+
+ private MacAddress mDeviceAddress = MAC_ANY_ADDRESS;
+ private String mNetworkName = "";
+ private String mPassphrase = "";
+ private int mGroupOwnerBand = GROUP_OWNER_BAND_AUTO;
+ private int mNetId = WifiP2pGroup.TEMPORARY_NET_ID;
+
+ /**
+ * Specify the peer's MAC address. If not set, the device will
+ * try to find a peer whose SSID matches the network name as
+ * specified by {@link #setNetworkName(String)}. Specifying null will
+ * reset the peer's MAC address to "00:00:00:00:00:00".
+ * <p>
+ * Optional. "00:00:00:00:00:00" by default.
+ *
+ * @param deviceAddress the peer's MAC address.
+ * @return The builder to facilitate chaining
+ * {@code builder.setXXX(..).setXXX(..)}.
+ */
+ public Builder setDeviceAddress(MacAddress deviceAddress) {
+ if (deviceAddress == null) {
+ mDeviceAddress = MAC_ANY_ADDRESS;
+ } else {
+ mDeviceAddress = deviceAddress;
+ }
+ return this;
+ }
+
+ /**
+ * Specify the network name, a.k.a. group name,
+ * for creating or joining a group.
+ * <p>
+ * Must be called - an empty network name is not valid.
+ *
+ * @param networkName network name of a group.
+ * @return The builder to facilitate chaining
+ * {@code builder.setXXX(..).setXXX(..)}.
+ */
+ public Builder setNetworkName(String networkName) {
+ if (TextUtils.isEmpty(networkName)) {
+ throw new IllegalArgumentException(
+ "network name must be non-empty.");
+ }
+ mNetworkName = networkName;
+ return this;
+ }
+
+ /**
+ * Specify the passphrase for creating or joining a group.
+ * <p>
+ * Must be called - an empty passphrase is not valid.
+ *
+ * @param passphrase the passphrase of a group.
+ * @return The builder to facilitate chaining
+ * {@code builder.setXXX(..).setXXX(..)}.
+ */
+ public Builder setPassphrase(String passphrase) {
+ if (TextUtils.isEmpty(passphrase)) {
+ throw new IllegalArgumentException(
+ "passphrase must be non-empty.");
+ }
+ mPassphrase = passphrase;
+ return this;
+ }
+
+ /**
+ * Specify the band to use for creating the group. This method only applies when
+ * creating a group as Group Owner using {@link WifiP2pManager#createGroup}.
+ * The band should be {@link #GROUP_OWNER_BAND_2GHZ} or {@link #GROUP_OWNER_BAND_5GHZ},
+ * or allow the system to pick the band by specifying {@link #GROUP_OWNER_BAND_AUTO}.
+ * If the Group Owner cannot create a group in the specified band, the operation will fail.
+ * <p>
+ * Optional. {@link #GROUP_OWNER_BAND_AUTO} by default.
+ *
+ * @param band the required band of group owner.
+ * This should be one of {@link #GROUP_OWNER_BAND_AUTO},
+ * {@link #GROUP_OWNER_BAND_2GHZ}, {@link #GROUP_OWNER_BAND_5GHZ}.
+ * @return The builder to facilitate chaining
+ * {@code builder.setXXX(..).setXXX(..)}.
+ */
+ public Builder setGroupOwnerBand(int band) {
+ mGroupOwnerBand = band;
+ return this;
+ }
+
+ /**
+ * Specify that the group configuration be persisted (i.e. saved).
+ * By default the group configuration will not be saved.
+ * <p>
+ * Optional. false by default.
+ *
+ * @param persistent is this group persistent group.
+ * @return The builder to facilitate chaining
+ * {@code builder.setXXX(..).setXXX(..)}.
+ */
+ public Builder enablePersistentMode(boolean persistent) {
+ if (persistent) {
+ mNetId = WifiP2pGroup.PERSISTENT_NET_ID;
+ } else {
+ mNetId = WifiP2pGroup.TEMPORARY_NET_ID;
+ }
+ return this;
+ }
+
+ /**
+ * Build {@link WifiP2pConfig} given the current requests made on the builder.
+ * @return {@link WifiP2pConfig} constructed based on builder method calls.
+ */
+ public WifiP2pConfig build() {
+ if (TextUtils.isEmpty(mNetworkName)) {
+ throw new IllegalStateException(
+ "network name must be non-empty.");
+ }
+ if (TextUtils.isEmpty(mPassphrase)) {
+ throw new IllegalStateException(
+ "passphrase must be non-empty.");
+ }
+
+ WifiP2pConfig config = new WifiP2pConfig();
+ config.deviceAddress = mDeviceAddress.toString();
+ config.networkName = mNetworkName;
+ config.passphrase = mPassphrase;
+ config.groupOwnerBand = mGroupOwnerBand;
+ config.netId = mNetId;
+ return config;
+ }
+ }
}
diff --git a/wifi/java/com/android/server/wifi/AbstractWifiService.java b/wifi/java/com/android/server/wifi/AbstractWifiService.java
index 04bc557..aa526d2 100644
--- a/wifi/java/com/android/server/wifi/AbstractWifiService.java
+++ b/wifi/java/com/android/server/wifi/AbstractWifiService.java
@@ -73,7 +73,7 @@
}
@Override
- public ParceledListSlice getConfiguredNetworks() {
+ public ParceledListSlice getConfiguredNetworks(String packageName) {
throw new UnsupportedOperationException();
}
@@ -188,17 +188,17 @@
}
@Override
- public void disconnect(String packageName) {
+ public boolean disconnect(String packageName) {
throw new UnsupportedOperationException();
}
@Override
- public void reconnect(String packageName) {
+ public boolean reconnect(String packageName) {
throw new UnsupportedOperationException();
}
@Override
- public void reassociate(String packageName) {
+ public boolean reassociate(String packageName) {
throw new UnsupportedOperationException();
}