Merge "Unhide RecoverableKeyStoreLoader API."
diff --git a/Android.bp b/Android.bp
index 9e9faf2..6cb1e5c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -147,9 +147,10 @@
"core/java/android/hardware/display/IDisplayManager.aidl",
"core/java/android/hardware/display/IDisplayManagerCallback.aidl",
"core/java/android/hardware/display/IVirtualDisplayCallback.aidl",
+ "core/java/android/hardware/fingerprint/IFingerprintClientActiveCallback.aidl",
+ "core/java/android/hardware/fingerprint/IFingerprintDialogReceiver.aidl",
"core/java/android/hardware/fingerprint/IFingerprintService.aidl",
"core/java/android/hardware/fingerprint/IFingerprintServiceLockoutResetCallback.aidl",
- "core/java/android/hardware/fingerprint/IFingerprintClientActiveCallback.aidl",
"core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl",
"core/java/android/hardware/hdmi/IHdmiControlCallback.aidl",
"core/java/android/hardware/hdmi/IHdmiControlService.aidl",
diff --git a/api/current.txt b/api/current.txt
index 283cd86..a948c97 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6377,6 +6377,7 @@
public class DevicePolicyManager {
method public void addCrossProfileIntentFilter(android.content.ComponentName, android.content.IntentFilter, int);
method public boolean addCrossProfileWidgetProvider(android.content.ComponentName, java.lang.String);
+ method public int addOverrideApn(android.content.ComponentName, android.telephony.data.ApnSetting);
method public void addPersistentPreferredActivity(android.content.ComponentName, android.content.IntentFilter, android.content.ComponentName);
method public void addUserRestriction(android.content.ComponentName, java.lang.String);
method public boolean bindDeviceAdminServiceAsUser(android.content.ComponentName, android.content.Intent, android.content.ServiceConnection, int, android.os.UserHandle);
@@ -6423,6 +6424,7 @@
method public java.util.List<java.lang.String> getMeteredDataDisabled(android.content.ComponentName);
method public int getOrganizationColor(android.content.ComponentName);
method public java.lang.CharSequence getOrganizationName(android.content.ComponentName);
+ method public java.util.List<android.telephony.data.ApnSetting> getOverrideApns(android.content.ComponentName);
method public android.app.admin.DevicePolicyManager getParentProfileInstance(android.content.ComponentName);
method public java.lang.String getPasswordBlacklistName(android.content.ComponentName);
method public long getPasswordExpiration(android.content.ComponentName);
@@ -6474,6 +6476,7 @@
method public boolean isManagedProfile(android.content.ComponentName);
method public boolean isMasterVolumeMuted(android.content.ComponentName);
method public boolean isNetworkLoggingEnabled(android.content.ComponentName);
+ method public boolean isOverrideApnEnabled(android.content.ComponentName);
method public boolean isPackageSuspended(android.content.ComponentName, java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
method public boolean isPrintingEnabled();
method public boolean isProfileOwnerApp(java.lang.String);
@@ -6489,6 +6492,7 @@
method public void removeActiveAdmin(android.content.ComponentName);
method public boolean removeCrossProfileWidgetProvider(android.content.ComponentName, java.lang.String);
method public boolean removeKeyPair(android.content.ComponentName, java.lang.String);
+ method public boolean removeOverrideApn(android.content.ComponentName, int);
method public boolean removeUser(android.content.ComponentName, android.os.UserHandle);
method public boolean requestBugreport(android.content.ComponentName);
method public boolean resetPassword(java.lang.String, int);
@@ -6529,6 +6533,7 @@
method public void setNetworkLoggingEnabled(android.content.ComponentName, boolean);
method public void setOrganizationColor(android.content.ComponentName, int);
method public void setOrganizationName(android.content.ComponentName, java.lang.CharSequence);
+ method public void setOverrideApnsEnabled(android.content.ComponentName, boolean);
method public java.lang.String[] setPackagesSuspended(android.content.ComponentName, java.lang.String[], boolean);
method public boolean setPasswordBlacklist(android.content.ComponentName, java.lang.String, java.util.List<java.lang.String>);
method public void setPasswordExpirationTimeout(android.content.ComponentName, long);
@@ -6573,6 +6578,7 @@
method public void transferOwnership(android.content.ComponentName, android.content.ComponentName, android.os.PersistableBundle);
method public void uninstallAllUserCaCerts(android.content.ComponentName);
method public void uninstallCaCert(android.content.ComponentName, byte[]);
+ method public boolean updateOverrideApn(android.content.ComponentName, int, android.telephony.data.ApnSetting);
method public void wipeData(int);
method public void wipeDataWithReason(int, java.lang.CharSequence);
field public static final java.lang.String ACTION_ADD_DEVICE_ADMIN = "android.app.action.ADD_DEVICE_ADMIN";
@@ -10873,7 +10879,8 @@
field public android.content.pm.ServiceInfo[] services;
field public java.lang.String sharedUserId;
field public int sharedUserLabel;
- field public android.content.pm.Signature[] signatures;
+ field public deprecated android.content.pm.Signature[] signatures;
+ field public android.content.pm.Signature[][] signingCertificateHistory;
field public java.lang.String[] splitNames;
field public int[] splitRevisionCodes;
field public deprecated int versionCode;
@@ -11076,6 +11083,8 @@
method public abstract android.graphics.drawable.Drawable getUserBadgedIcon(android.graphics.drawable.Drawable, android.os.UserHandle);
method public abstract java.lang.CharSequence getUserBadgedLabel(java.lang.CharSequence, android.os.UserHandle);
method public abstract android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
+ method public boolean hasSigningCertificate(java.lang.String, byte[], int);
+ method public boolean hasSigningCertificate(int, byte[], int);
method public abstract boolean hasSystemFeature(java.lang.String);
method public abstract boolean hasSystemFeature(java.lang.String, int);
method public abstract boolean isInstantApp();
@@ -11101,6 +11110,8 @@
method public abstract void setInstallerPackageName(java.lang.String, java.lang.String);
method public abstract void updateInstantAppCookie(byte[]);
method public abstract void verifyPendingInstall(int, int);
+ field public static final int CERT_INPUT_RAW_X509 = 0; // 0x0
+ field public static final int CERT_INPUT_SHA256 = 1; // 0x1
field public static final int COMPONENT_ENABLED_STATE_DEFAULT = 0; // 0x0
field public static final int COMPONENT_ENABLED_STATE_DISABLED = 2; // 0x2
field public static final int COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED = 4; // 0x4
@@ -11219,7 +11230,8 @@
field public static final int GET_RESOLVED_FILTER = 64; // 0x40
field public static final int GET_SERVICES = 4; // 0x4
field public static final int GET_SHARED_LIBRARY_FILES = 1024; // 0x400
- field public static final int GET_SIGNATURES = 64; // 0x40
+ field public static final deprecated int GET_SIGNATURES = 64; // 0x40
+ field public static final int GET_SIGNING_CERTIFICATES = 134217728; // 0x8000000
field public static final deprecated int GET_UNINSTALLED_PACKAGES = 8192; // 0x2000
field public static final int GET_URI_PERMISSION_PATTERNS = 2048; // 0x800
field public static final int INSTALL_REASON_DEVICE_RESTORE = 2; // 0x2
@@ -16396,6 +16408,20 @@
package android.hardware.fingerprint {
+ public class FingerprintDialog {
+ method public void authenticate(android.hardware.fingerprint.FingerprintManager.CryptoObject, android.os.CancellationSignal, java.util.concurrent.Executor, android.hardware.fingerprint.FingerprintManager.AuthenticationCallback);
+ method public void authenticate(android.os.CancellationSignal, java.util.concurrent.Executor, android.hardware.fingerprint.FingerprintManager.AuthenticationCallback);
+ }
+
+ public static class FingerprintDialog.Builder {
+ ctor public FingerprintDialog.Builder();
+ method public android.hardware.fingerprint.FingerprintDialog build(android.content.Context);
+ method public android.hardware.fingerprint.FingerprintDialog.Builder setDescription(java.lang.CharSequence);
+ method public android.hardware.fingerprint.FingerprintDialog.Builder setNegativeButton(java.lang.CharSequence, java.util.concurrent.Executor, android.content.DialogInterface.OnClickListener);
+ method public android.hardware.fingerprint.FingerprintDialog.Builder setSubtitle(java.lang.CharSequence);
+ method public android.hardware.fingerprint.FingerprintDialog.Builder setTitle(java.lang.CharSequence);
+ }
+
public class FingerprintManager {
method public void authenticate(android.hardware.fingerprint.FingerprintManager.CryptoObject, android.os.CancellationSignal, int, android.hardware.fingerprint.FingerprintManager.AuthenticationCallback, android.os.Handler);
method public boolean hasEnrolledFingerprints();
@@ -35904,6 +35930,7 @@
field public static final java.lang.String ACTION_SETTINGS = "android.settings.SETTINGS";
field public static final java.lang.String ACTION_SHOW_REGULATORY_INFO = "android.settings.SHOW_REGULATORY_INFO";
field public static final java.lang.String ACTION_SOUND_SETTINGS = "android.settings.SOUND_SETTINGS";
+ field public static final java.lang.String ACTION_STORAGE_VOLUME_ACCESS_SETTINGS = "android.settings.STORAGE_VOLUME_ACCESS_SETTINGS";
field public static final java.lang.String ACTION_SYNC_SETTINGS = "android.settings.SYNC_SETTINGS";
field public static final java.lang.String ACTION_USAGE_ACCESS_SETTINGS = "android.settings.USAGE_ACCESS_SETTINGS";
field public static final java.lang.String ACTION_USER_DICTIONARY_SETTINGS = "android.settings.USER_DICTIONARY_SETTINGS";
@@ -45744,6 +45771,7 @@
field public static final int KEYCODE_PROG_YELLOW = 185; // 0xb9
field public static final int KEYCODE_Q = 45; // 0x2d
field public static final int KEYCODE_R = 46; // 0x2e
+ field public static final int KEYCODE_REFRESH = 285; // 0x11d
field public static final int KEYCODE_RIGHT_BRACKET = 72; // 0x48
field public static final int KEYCODE_RO = 217; // 0xd9
field public static final int KEYCODE_S = 47; // 0x2f
diff --git a/api/system-current.txt b/api/system-current.txt
index dcbe444..14320c4 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -2871,6 +2871,7 @@
}
public final class IpSecManager {
+ method public void applyTunnelModeTransform(android.net.IpSecManager.IpSecTunnelInterface, int, android.net.IpSecTransform) throws java.io.IOException;
method public android.net.IpSecManager.IpSecTunnelInterface createIpSecTunnelInterface(java.net.InetAddress, java.net.InetAddress, android.net.Network) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
}
diff --git a/api/test-current.txt b/api/test-current.txt
index acc819e..254fc15 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -521,6 +521,7 @@
}
public static final class Settings.Global extends android.provider.Settings.NameValueTable {
+ field public static final java.lang.String LOCATION_GLOBAL_KILL_SWITCH = "location_global_kill_switch";
field public static final java.lang.String LOW_POWER_MODE = "low_power";
field public static final java.lang.String USE_OPEN_WIFI_PACKAGE = "use_open_wifi_package";
}
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index ca097d0..ba628b8 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -725,7 +725,7 @@
if (checkCallingPermission(String16(kPermissionDump))) {
ConfigKey configKey(ipc->getCallingUid(), key);
StatsdConfig cfg;
- if (!cfg.ParseFromArray(&config[0], config.size())) {
+ if (config.empty() || !cfg.ParseFromArray(&config[0], config.size())) {
*success = false;
return Status::ok();
}
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 49d5224..cc68c05 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -699,6 +699,26 @@
}
@Override
+ public boolean hasSigningCertificate(
+ String packageName, byte[] certificate, @PackageManager.CertificateInputType int type) {
+ try {
+ return mPM.hasSigningCertificate(packageName, certificate, type);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ @Override
+ public boolean hasSigningCertificate(
+ int uid, byte[] certificate, @PackageManager.CertificateInputType int type) {
+ try {
+ return mPM.hasUidSigningCertificate(uid, certificate, type);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ @Override
public String[] getPackagesForUid(int uid) {
try {
return mPM.getPackagesForUid(uid);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 2e3b8af..d6fddfc 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -4130,7 +4130,7 @@
final Bundle ex = mN.extras;
updateBackgroundColor(contentView);
bindNotificationHeader(contentView, p.ambient, p.headerTextSecondary);
- bindLargeIcon(contentView, p.hideLargeIcon, p.alwaysShowReply);
+ bindLargeIcon(contentView, p.hideLargeIcon || p.ambient, p.alwaysShowReply);
boolean showProgress = handleProgressBar(p.hasProgress, contentView, ex);
if (p.title != null) {
contentView.setViewVisibility(R.id.title, View.VISIBLE);
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index c06ad3f..30f2697 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -32,8 +32,6 @@
import com.android.internal.util.Preconditions;
-import com.android.internal.util.Preconditions;
-
import org.json.JSONException;
import org.json.JSONObject;
import org.xmlpull.v1.XmlPullParser;
@@ -936,7 +934,9 @@
}
/** @hide */
- public void toProto(ProtoOutputStream proto) {
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+
proto.write(NotificationChannelProto.ID, mId);
proto.write(NotificationChannelProto.NAME, mName);
proto.write(NotificationChannelProto.DESCRIPTION, mDesc);
@@ -959,10 +959,10 @@
proto.write(NotificationChannelProto.IS_DELETED, mDeleted);
proto.write(NotificationChannelProto.GROUP, mGroup);
if (mAudioAttributes != null) {
- long aToken = proto.start(NotificationChannelProto.AUDIO_ATTRIBUTES);
- mAudioAttributes.toProto(proto);
- proto.end(aToken);
+ mAudioAttributes.writeToProto(proto, NotificationChannelProto.AUDIO_ATTRIBUTES);
}
proto.write(NotificationChannelProto.IS_BLOCKABLE_SYSTEM, mBlockableSystem);
+
+ proto.end(token);
}
}
diff --git a/core/java/android/app/NotificationChannelGroup.java b/core/java/android/app/NotificationChannelGroup.java
index 5cb7fb7..16166f7 100644
--- a/core/java/android/app/NotificationChannelGroup.java
+++ b/core/java/android/app/NotificationChannelGroup.java
@@ -298,13 +298,17 @@
}
/** @hide */
- public void toProto(ProtoOutputStream proto) {
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+
proto.write(NotificationChannelGroupProto.ID, mId);
proto.write(NotificationChannelGroupProto.NAME, mName.toString());
proto.write(NotificationChannelGroupProto.DESCRIPTION, mDescription);
proto.write(NotificationChannelGroupProto.IS_BLOCKED, mBlocked);
for (NotificationChannel channel : mChannels) {
- channel.toProto(proto);
+ channel.writeToProto(proto, NotificationChannelGroupProto.CHANNELS);
}
+
+ proto.end(token);
}
}
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 659cf16..45bed5d 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -1133,7 +1133,7 @@
}
/** @hide */
- public void toProto(ProtoOutputStream proto, long fieldId) {
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
final long pToken = proto.start(fieldId);
bitwiseToProtoEnum(proto, PolicyProto.PRIORITY_CATEGORIES, priorityCategories);
diff --git a/core/java/android/app/ProfilerInfo.java b/core/java/android/app/ProfilerInfo.java
index a295c4c..0ed1b08 100644
--- a/core/java/android/app/ProfilerInfo.java
+++ b/core/java/android/app/ProfilerInfo.java
@@ -20,6 +20,7 @@
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
import java.io.IOException;
import java.util.Objects;
@@ -124,6 +125,20 @@
out.writeBoolean(attachAgentDuringBind);
}
+ /** @hide */
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(ProfilerInfoProto.PROFILE_FILE, profileFile);
+ if (profileFd != null) {
+ proto.write(ProfilerInfoProto.PROFILE_FD, profileFd.getFd());
+ }
+ proto.write(ProfilerInfoProto.SAMPLING_INTERVAL, samplingInterval);
+ proto.write(ProfilerInfoProto.AUTO_STOP_PROFILER, autoStopProfiler);
+ proto.write(ProfilerInfoProto.STREAMING_OUTPUT, streamingOutput);
+ proto.write(ProfilerInfoProto.AGENT, agent);
+ proto.end(token);
+ }
+
public static final Parcelable.Creator<ProfilerInfo> CREATOR =
new Parcelable.Creator<ProfilerInfo>() {
@Override
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index bad19e3..52870b3 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -68,6 +68,7 @@
import android.security.keystore.ParcelableKeyGenParameterSpec;
import android.service.restrictions.RestrictionsReceiver;
import android.telephony.TelephonyManager;
+import android.telephony.data.ApnSetting;
import android.util.ArraySet;
import android.util.Log;
@@ -9298,4 +9299,138 @@
throw re.rethrowFromSystemServer();
}
}
+
+ /**
+ * Called by device owner to add an override APN.
+ *
+ * @param admin which {@link DeviceAdminReceiver} this request is associated with
+ * @param apnSetting the override APN to insert
+ * @return The {@code id} of inserted override APN. Or {@code -1} when failed to insert into
+ * the database.
+ * @throws SecurityException if {@code admin} is not a device owner.
+ *
+ * @see #setOverrideApnsEnabled(ComponentName, boolean)
+ */
+ public int addOverrideApn(@NonNull ComponentName admin, @NonNull ApnSetting apnSetting) {
+ throwIfParentInstance("addOverrideApn");
+ if (mService != null) {
+ try {
+ return mService.addOverrideApn(admin, apnSetting);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Called by device owner to update an override APN.
+ *
+ * @param admin which {@link DeviceAdminReceiver} this request is associated with
+ * @param apnId the {@code id} of the override APN to update
+ * @param apnSetting the override APN to update
+ * @return {@code true} if the required override APN is successfully updated,
+ * {@code false} otherwise.
+ * @throws SecurityException if {@code admin} is not a device owner.
+ *
+ * @see #setOverrideApnsEnabled(ComponentName, boolean)
+ */
+ public boolean updateOverrideApn(@NonNull ComponentName admin, int apnId,
+ @NonNull ApnSetting apnSetting) {
+ throwIfParentInstance("updateOverrideApn");
+ if (mService != null) {
+ try {
+ return mService.updateOverrideApn(admin, apnId, apnSetting);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Called by device owner to remove an override APN.
+ *
+ * @param admin which {@link DeviceAdminReceiver} this request is associated with
+ * @param apnId the {@code id} of the override APN to remove
+ * @return {@code true} if the required override APN is successfully removed, {@code false}
+ * otherwise.
+ * @throws SecurityException if {@code admin} is not a device owner.
+ *
+ * @see #setOverrideApnsEnabled(ComponentName, boolean)
+ */
+ public boolean removeOverrideApn(@NonNull ComponentName admin, int apnId) {
+ throwIfParentInstance("removeOverrideApn");
+ if (mService != null) {
+ try {
+ return mService.removeOverrideApn(admin, apnId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Called by device owner to get all override APNs inserted by device owner.
+ *
+ * @param admin which {@link DeviceAdminReceiver} this request is associated with
+ * @return A list of override APNs inserted by device owner.
+ * @throws SecurityException if {@code admin} is not a device owner.
+ *
+ * @see #setOverrideApnsEnabled(ComponentName, boolean)
+ */
+ public List<ApnSetting> getOverrideApns(@NonNull ComponentName admin) {
+ throwIfParentInstance("getOverrideApns");
+ if (mService != null) {
+ try {
+ return mService.getOverrideApns(admin);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return Collections.emptyList();
+ }
+
+ /**
+ * Called by device owner to set if override APNs should be enabled.
+ * <p> Override APNs are separated from other APNs on the device, and can only be inserted or
+ * modified by the device owner. When enabled, only override APNs are in use, any other APNs
+ * are ignored.
+ *
+ * @param admin which {@link DeviceAdminReceiver} this request is associated with
+ * @param enabled {@code true} if override APNs should be enabled, {@code false} otherwise
+ * @throws SecurityException if {@code admin} is not a device owner.
+ */
+ public void setOverrideApnsEnabled(@NonNull ComponentName admin, boolean enabled) {
+ throwIfParentInstance("setOverrideApnEnabled");
+ if (mService != null) {
+ try {
+ mService.setOverrideApnsEnabled(admin, enabled);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Called by device owner to check if override APNs are currently enabled.
+ *
+ * @param admin which {@link DeviceAdminReceiver} this request is associated with
+ * @return {@code true} if override APNs are currently enabled, {@code false} otherwise.
+ * @throws SecurityException if {@code admin} is not a device owner.
+ *
+ * @see #setOverrideApnsEnabled(ComponentName, boolean)
+ */
+ public boolean isOverrideApnEnabled(@NonNull ComponentName admin) {
+ throwIfParentInstance("isOverrideApnEnabled");
+ if (mService != null) {
+ try {
+ return mService.isOverrideApnEnabled(admin);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return false;
+ }
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 514dca9..a5ca4cf 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -38,6 +38,7 @@
import android.os.UserHandle;
import android.security.keymaster.KeymasterCertificateChain;
import android.security.keystore.ParcelableKeyGenParameterSpec;
+import android.telephony.data.ApnSetting;
import java.util.List;
@@ -403,4 +404,11 @@
List<String> setMeteredDataDisabled(in ComponentName admin, in List<String> packageNames);
List<String> getMeteredDataDisabled(in ComponentName admin);
+
+ int addOverrideApn(in ComponentName admin, in ApnSetting apnSetting);
+ boolean updateOverrideApn(in ComponentName admin, int apnId, in ApnSetting apnSetting);
+ boolean removeOverrideApn(in ComponentName admin, int apnId);
+ List<ApnSetting> getOverrideApns(in ComponentName admin);
+ void setOverrideApnsEnabled(in ComponentName admin, boolean enabled);
+ boolean isOverrideApnEnabled(in ComponentName admin);
}
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index f04e907..edb992b 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -106,6 +106,12 @@
*/
public static final int NOTIFICATION_SEEN = 10;
+ /**
+ * An event type denoting a change in App Standby Bucket.
+ * @hide
+ */
+ public static final int STANDBY_BUCKET_CHANGED = 11;
+
/** @hide */
public static final int FLAG_IS_PACKAGE_INSTANT_APP = 1 << 0;
@@ -170,6 +176,13 @@
*/
public String[] mContentAnnotations;
+ /**
+ * The app standby bucket assigned.
+ * Only present for {@link #STANDBY_BUCKET_CHANGED} event types
+ * {@hide}
+ */
+ public int mBucket;
+
/** @hide */
@EventFlags
public int mFlags;
@@ -189,6 +202,7 @@
mContentType = orig.mContentType;
mContentAnnotations = orig.mContentAnnotations;
mFlags = orig.mFlags;
+ mBucket = orig.mBucket;
}
/**
@@ -399,6 +413,9 @@
p.writeString(event.mContentType);
p.writeStringArray(event.mContentAnnotations);
break;
+ case Event.STANDBY_BUCKET_CHANGED:
+ p.writeInt(event.mBucket);
+ break;
}
}
@@ -442,6 +459,9 @@
eventOut.mContentType = p.readString();
eventOut.mContentAnnotations = p.createStringArray();
break;
+ case Event.STANDBY_BUCKET_CHANGED:
+ eventOut.mBucket = p.readInt();
+ break;
}
}
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 84cbdb4..7bdf72f 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -34,6 +34,7 @@
import android.text.TextUtils;
import android.util.Printer;
import android.util.SparseArray;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.util.ArrayUtils;
@@ -1183,6 +1184,105 @@
super.dumpBack(pw, prefix);
}
+ /** {@hide} */
+ public void writeToProto(ProtoOutputStream proto, long fieldId, int dumpFlags) {
+ long token = proto.start(fieldId);
+ super.writeToProto(proto, ApplicationInfoProto.PACKAGE);
+ proto.write(ApplicationInfoProto.PERMISSION, permission);
+ proto.write(ApplicationInfoProto.PROCESS_NAME, processName);
+ proto.write(ApplicationInfoProto.UID, uid);
+ proto.write(ApplicationInfoProto.FLAGS, flags);
+ proto.write(ApplicationInfoProto.PRIVATE_FLAGS, privateFlags);
+ proto.write(ApplicationInfoProto.THEME, theme);
+ proto.write(ApplicationInfoProto.SOURCE_DIR, sourceDir);
+ if (!Objects.equals(sourceDir, publicSourceDir)) {
+ proto.write(ApplicationInfoProto.PUBLIC_SOURCE_DIR, publicSourceDir);
+ }
+ if (!ArrayUtils.isEmpty(splitSourceDirs)) {
+ for (String dir : splitSourceDirs) {
+ proto.write(ApplicationInfoProto.SPLIT_SOURCE_DIRS, dir);
+ }
+ }
+ if (!ArrayUtils.isEmpty(splitPublicSourceDirs)
+ && !Arrays.equals(splitSourceDirs, splitPublicSourceDirs)) {
+ for (String dir : splitPublicSourceDirs) {
+ proto.write(ApplicationInfoProto.SPLIT_PUBLIC_SOURCE_DIRS, dir);
+ }
+ }
+ if (resourceDirs != null) {
+ for (String dir : resourceDirs) {
+ proto.write(ApplicationInfoProto.RESOURCE_DIRS, dir);
+ }
+ }
+ proto.write(ApplicationInfoProto.DATA_DIR, dataDir);
+ proto.write(ApplicationInfoProto.CLASS_LOADER_NAME, classLoaderName);
+ if (!ArrayUtils.isEmpty(splitClassLoaderNames)) {
+ for (String name : splitClassLoaderNames) {
+ proto.write(ApplicationInfoProto.SPLIT_CLASS_LOADER_NAMES, name);
+ }
+ }
+
+ long versionToken = proto.start(ApplicationInfoProto.VERSION);
+ proto.write(ApplicationInfoProto.Version.ENABLED, enabled);
+ proto.write(ApplicationInfoProto.Version.MIN_SDK_VERSION, minSdkVersion);
+ proto.write(ApplicationInfoProto.Version.TARGET_SDK_VERSION, targetSdkVersion);
+ proto.write(ApplicationInfoProto.Version.VERSION_CODE, versionCode);
+ proto.write(ApplicationInfoProto.Version.TARGET_SANDBOX_VERSION, targetSandboxVersion);
+ proto.end(versionToken);
+
+ if ((dumpFlags & DUMP_FLAG_DETAILS) != 0) {
+ long detailToken = proto.start(ApplicationInfoProto.DETAIL);
+ if (className != null) {
+ proto.write(ApplicationInfoProto.Detail.CLASS_NAME, className);
+ }
+ proto.write(ApplicationInfoProto.Detail.TASK_AFFINITY, taskAffinity);
+ proto.write(ApplicationInfoProto.Detail.REQUIRES_SMALLEST_WIDTH_DP,
+ requiresSmallestWidthDp);
+ proto.write(ApplicationInfoProto.Detail.COMPATIBLE_WIDTH_LIMIT_DP,
+ compatibleWidthLimitDp);
+ proto.write(ApplicationInfoProto.Detail.LARGEST_WIDTH_LIMIT_DP,
+ largestWidthLimitDp);
+ if (seInfo != null) {
+ proto.write(ApplicationInfoProto.Detail.SEINFO, seInfo);
+ proto.write(ApplicationInfoProto.Detail.SEINFO_USER, seInfoUser);
+ }
+ proto.write(ApplicationInfoProto.Detail.DEVICE_PROTECTED_DATA_DIR,
+ deviceProtectedDataDir);
+ proto.write(ApplicationInfoProto.Detail.CREDENTIAL_PROTECTED_DATA_DIR,
+ credentialProtectedDataDir);
+ if (sharedLibraryFiles != null) {
+ for (String f : sharedLibraryFiles) {
+ proto.write(ApplicationInfoProto.Detail.SHARED_LIBRARY_FILES, f);
+ }
+ }
+ if (manageSpaceActivityName != null) {
+ proto.write(ApplicationInfoProto.Detail.MANAGE_SPACE_ACTIVITY_NAME,
+ manageSpaceActivityName);
+ }
+ if (descriptionRes != 0) {
+ proto.write(ApplicationInfoProto.Detail.DESCRIPTION_RES, descriptionRes);
+ }
+ if (uiOptions != 0) {
+ proto.write(ApplicationInfoProto.Detail.UI_OPTIONS, uiOptions);
+ }
+ proto.write(ApplicationInfoProto.Detail.SUPPORTS_RTL, hasRtlSupport());
+ if (fullBackupContent > 0) {
+ proto.write(ApplicationInfoProto.Detail.CONTENT, "@xml/" + fullBackupContent);
+ } else {
+ proto.write(ApplicationInfoProto.Detail.IS_FULL_BACKUP, fullBackupContent == 0);
+ }
+ if (networkSecurityConfigRes != 0) {
+ proto.write(ApplicationInfoProto.Detail.NETWORK_SECURITY_CONFIG_RES,
+ networkSecurityConfigRes);
+ }
+ if (category != CATEGORY_UNDEFINED) {
+ proto.write(ApplicationInfoProto.Detail.CATEGORY, category);
+ }
+ proto.end(detailToken);
+ }
+ proto.end(token);
+ }
+
/**
* @return true if "supportsRtl" has been set to true in the AndroidManifest
* @hide
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index cce6b84..379bff4 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -656,4 +656,8 @@
void setHarmfulAppWarning(String packageName, CharSequence warning, int userId);
CharSequence getHarmfulAppWarning(String packageName, int userId);
+
+ boolean hasSigningCertificate(String packageName, in byte[] signingCertificate, int flags);
+
+ boolean hasUidSigningCertificate(int uid, in byte[] signingCertificate, int flags);
}
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index 5a91e94..13ec4fd 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -246,9 +246,44 @@
* equivalent to being signed with certificates B and A. This means that
* in case multiple signatures are reported you cannot assume the one at
* the first position to be the same across updates.
+ *
+ * <strong>Deprecated</strong> This has been replaced by the
+ * {@link PackageInfo#signingCertificateHistory} field, which takes into
+ * account signing certificate rotation. For backwards compatibility in
+ * the event of signing certificate rotation, this will return the oldest
+ * reported signing certificate, so that an application will appear to
+ * callers as though no rotation occurred.
+ *
+ * @deprecated use {@code signingCertificateHistory} instead
*/
+ @Deprecated
public Signature[] signatures;
-
+
+ /**
+ * Array of all signatures arrays read from the package file, potentially
+ * including past signing certificates no longer used after signing
+ * certificate rotation. Though signing certificate rotation is only
+ * available for apps with a single signing certificate, this provides an
+ * array of arrays so that packages signed with multiple signing
+ * certificates can still return all signers. This is only filled in if
+ * the flag {@link PackageManager#GET_SIGNING_CERTIFICATES} was set.
+ *
+ * A package must be singed with at least one certificate, which is at
+ * position zero in the array. An application may be signed by multiple
+ * certificates, which would be in the array at position zero in an
+ * indeterminate order. A package may also have a history of certificates
+ * due to signing certificate rotation. In this case, the array will be
+ * populated by a series of single-entry arrays corresponding to a signing
+ * certificate of the package.
+ *
+ * <strong>Note:</strong> Signature ordering is not guaranteed to be
+ * stable which means that a package signed with certificates A and B is
+ * equivalent to being signed with certificates B and A. This means that
+ * in case multiple signatures are reported you cannot assume the one at
+ * the first position will be the same across updates.
+ */
+ public Signature[][] signingCertificateHistory;
+
/**
* Application specified preferred configuration
* {@link android.R.styleable#AndroidManifestUsesConfiguration
diff --git a/core/java/android/content/pm/PackageItemInfo.java b/core/java/android/content/pm/PackageItemInfo.java
index 11830c2..2c0c6ad0 100644
--- a/core/java/android/content/pm/PackageItemInfo.java
+++ b/core/java/android/content/pm/PackageItemInfo.java
@@ -19,7 +19,6 @@
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.content.res.XmlResourceParser;
-
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Parcel;
@@ -28,6 +27,8 @@
import android.text.TextPaint;
import android.text.TextUtils;
import android.util.Printer;
+import android.util.proto.ProtoOutputStream;
+
import java.text.Collator;
import java.util.Comparator;
@@ -386,6 +387,24 @@
dest.writeInt(showUserIcon);
}
+ /**
+ * @hide
+ */
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ long token = proto.start(fieldId);
+ if (name != null) {
+ proto.write(PackageItemInfoProto.NAME, name);
+ }
+ proto.write(PackageItemInfoProto.PACKAGE_NAME, packageName);
+ if (labelRes != 0 || nonLocalizedLabel != null || icon != 0 || banner != 0) {
+ proto.write(PackageItemInfoProto.LABEL_RES, labelRes);
+ proto.write(PackageItemInfoProto.NON_LOCALIZED_LABEL, nonLocalizedLabel.toString());
+ proto.write(PackageItemInfoProto.ICON, icon);
+ proto.write(PackageItemInfoProto.BANNER, banner);
+ }
+ proto.end(token);
+ }
+
protected PackageItemInfo(Parcel source) {
name = source.readString();
packageName = source.readString();
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index b81267a..67c9584 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -133,6 +133,7 @@
GET_SERVICES,
GET_SHARED_LIBRARY_FILES,
GET_SIGNATURES,
+ GET_SIGNING_CERTIFICATES,
GET_URI_PERMISSION_PATTERNS,
MATCH_UNINSTALLED_PACKAGES,
MATCH_DISABLED_COMPONENTS,
@@ -272,7 +273,10 @@
/**
* {@link PackageInfo} flag: return information about the
* signatures included in the package.
+ *
+ * @deprecated use {@code GET_SIGNING_CERTIFICATES} instead
*/
+ @Deprecated
public static final int GET_SIGNATURES = 0x00000040;
/**
@@ -488,6 +492,14 @@
public static final int MATCH_STATIC_SHARED_LIBRARIES = 0x04000000;
/**
+ * {@link PackageInfo} flag: return the signing certificates associated with
+ * this package. Each entry is a signing certificate that the package
+ * has proven it is authorized to use, usually a past signing certificate from
+ * which it has rotated.
+ */
+ public static final int GET_SIGNING_CERTIFICATES = 0x08000000;
+
+ /**
* Internal flag used to indicate that a system component has done their
* homework and verified that they correctly handle packages and components
* that come and go over time. In particular:
@@ -3781,7 +3793,7 @@
public abstract int getInstantAppCookieMaxBytes();
/**
- * @deprecated
+ * deprecated
* @hide
*/
public abstract int getInstantAppCookieMaxSize();
@@ -5914,4 +5926,60 @@
public CharSequence getHarmfulAppWarning(@NonNull String packageName) {
throw new UnsupportedOperationException("getHarmfulAppWarning not implemented in subclass");
}
+
+ /** @hide */
+ @IntDef(prefix = { "CERT_INPUT_" }, value = {
+ CERT_INPUT_RAW_X509,
+ CERT_INPUT_SHA256
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CertificateInputType {}
+
+ /**
+ * Certificate input bytes: the input bytes represent an encoded X.509 Certificate which could
+ * be generated using an {@code CertificateFactory}
+ */
+ public static final int CERT_INPUT_RAW_X509 = 0;
+
+ /**
+ * Certificate input bytes: the input bytes represent the SHA256 output of an encoded X.509
+ * Certificate.
+ */
+ public static final int CERT_INPUT_SHA256 = 1;
+
+ /**
+ * Searches the set of signing certificates by which the given package has proven to have been
+ * signed. This should be used instead of {@code getPackageInfo} with {@code GET_SIGNATURES}
+ * since it takes into account the possibility of signing certificate rotation, except in the
+ * case of packages that are signed by multiple certificates, for which signing certificate
+ * rotation is not supported.
+ *
+ * @param packageName package whose signing certificates to check
+ * @param certificate signing certificate for which to search
+ * @param type representation of the {@code certificate}
+ * @return true if this package was or is signed by exactly the certificate {@code certificate}
+ */
+ public boolean hasSigningCertificate(
+ String packageName, byte[] certificate, @CertificateInputType int type) {
+ throw new UnsupportedOperationException(
+ "hasSigningCertificate not implemented in subclass");
+ }
+
+ /**
+ * Searches the set of signing certificates by which the given uid has proven to have been
+ * signed. This should be used instead of {@code getPackageInfo} with {@code GET_SIGNATURES}
+ * since it takes into account the possibility of signing certificate rotation, except in the
+ * case of packages that are signed by multiple certificates, for which signing certificate
+ * rotation is not supported.
+ *
+ * @param uid package whose signing certificates to check
+ * @param certificate signing certificate for which to search
+ * @param type representation of the {@code certificate}
+ * @return true if this package was or is signed by exactly the certificate {@code certificate}
+ */
+ public boolean hasSigningCertificate(
+ int uid, byte[] certificate, @CertificateInputType int type) {
+ throw new UnsupportedOperationException(
+ "hasSigningCertificate not implemented in subclass");
+ }
}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 4efd081..5b5ccf5 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -801,13 +801,40 @@
}
}
}
+ // deprecated method of getting signing certificates
if ((flags&PackageManager.GET_SIGNATURES) != 0) {
- if (p.mSigningDetails.hasSignatures()) {
+ if (p.mSigningDetails.hasPastSigningCertificates()) {
+ // Package has included signing certificate rotation information. Return the oldest
+ // cert so that programmatic checks keep working even if unaware of key rotation.
+ pi.signatures = new Signature[1];
+ pi.signatures[0] = p.mSigningDetails.pastSigningCertificates[0];
+ } else if (p.mSigningDetails.hasSignatures()) {
+ // otherwise keep old behavior
int numberOfSigs = p.mSigningDetails.signatures.length;
pi.signatures = new Signature[numberOfSigs];
System.arraycopy(p.mSigningDetails.signatures, 0, pi.signatures, 0, numberOfSigs);
}
}
+
+ // replacement for GET_SIGNATURES
+ if ((flags & PackageManager.GET_SIGNING_CERTIFICATES) != 0) {
+ if (p.mSigningDetails.hasPastSigningCertificates()) {
+ // Package has included signing certificate rotation information. Convert each
+ // entry to an array
+ int numberOfSigs = p.mSigningDetails.pastSigningCertificates.length;
+ pi.signingCertificateHistory = new Signature[numberOfSigs][];
+ for (int i = 0; i < numberOfSigs; i++) {
+ pi.signingCertificateHistory[i] =
+ new Signature[] { p.mSigningDetails.pastSigningCertificates[i] };
+ }
+ } else if (p.mSigningDetails.hasSignatures()) {
+ // otherwise keep old behavior
+ int numberOfSigs = p.mSigningDetails.signatures.length;
+ pi.signingCertificateHistory = new Signature[1][numberOfSigs];
+ System.arraycopy(p.mSigningDetails.signatures, 0,
+ pi.signingCertificateHistory[0], 0, numberOfSigs);
+ }
+ }
return pi;
}
@@ -5759,6 +5786,11 @@
return signatures != null && signatures.length > 0;
}
+ /** Returns true if the signing details have past signing certificates. */
+ public boolean hasPastSigningCertificates() {
+ return pastSigningCertificates != null && pastSigningCertificates.length > 0;
+ }
+
/** Returns true if the signatures in this and other match exactly. */
public boolean signaturesMatchExactly(SigningDetails other) {
return Signature.areExactMatch(this.signatures, other.signatures);
diff --git a/core/java/android/hardware/fingerprint/FingerprintDialog.java b/core/java/android/hardware/fingerprint/FingerprintDialog.java
new file mode 100644
index 0000000..6b7fab7
--- /dev/null
+++ b/core/java/android/hardware/fingerprint/FingerprintDialog.java
@@ -0,0 +1,293 @@
+/*
+ * 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.hardware.fingerprint;
+
+import static android.Manifest.permission.USE_FINGERPRINT;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback;
+import android.hardware.fingerprint.FingerprintManager.AuthenticationResult;
+import android.hardware.fingerprint.IFingerprintDialogReceiver;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.text.TextUtils;
+
+import java.util.concurrent.Executor;
+
+/**
+ * A class that manages a system-provided fingerprint dialog.
+ */
+public class FingerprintDialog {
+
+ /**
+ * @hide
+ */
+ public static final String KEY_TITLE = "title";
+ /**
+ * @hide
+ */
+ public static final String KEY_SUBTITLE = "subtitle";
+ /**
+ * @hide
+ */
+ public static final String KEY_DESCRIPTION = "description";
+ /**
+ * @hide
+ */
+ public static final String KEY_POSITIVE_TEXT = "positive_text";
+ /**
+ * @hide
+ */
+ public static final String KEY_NEGATIVE_TEXT = "negative_text";
+
+ /**
+ * Error/help message will show for this amount of time.
+ * For error messages, the dialog will also be dismissed after this amount of time.
+ * Error messages will be propagated back to the application via AuthenticationCallback
+ * after this amount of time.
+ * @hide
+ */
+ public static final int HIDE_DIALOG_DELAY = 3000; // ms
+ /**
+ * @hide
+ */
+ public static final int DISMISSED_REASON_POSITIVE = 1;
+
+ /**
+ * @hide
+ */
+ public static final int DISMISSED_REASON_NEGATIVE = 2;
+
+ /**
+ * @hide
+ */
+ public static final int DISMISSED_REASON_USER_CANCEL = 3;
+
+ private static class ButtonInfo {
+ Executor executor;
+ DialogInterface.OnClickListener listener;
+ ButtonInfo(Executor ex, DialogInterface.OnClickListener l) {
+ executor = ex;
+ listener = l;
+ }
+ }
+
+ /**
+ * A builder that collects arguments, to be shown on the system-provided fingerprint dialog.
+ **/
+ public static class Builder {
+ private final Bundle bundle;
+ private ButtonInfo positiveButtonInfo;
+ private ButtonInfo negativeButtonInfo;
+
+ /**
+ * Creates a builder for a fingerprint dialog.
+ */
+ public Builder() {
+ bundle = new Bundle();
+ }
+
+ /**
+ * Required: Set the title to display.
+ * @param title
+ * @return
+ */
+ public Builder setTitle(@NonNull CharSequence title) {
+ bundle.putCharSequence(KEY_TITLE, title);
+ return this;
+ }
+
+ /**
+ * Optional: Set the subtitle to display.
+ * @param subtitle
+ * @return
+ */
+ public Builder setSubtitle(@NonNull CharSequence subtitle) {
+ bundle.putCharSequence(KEY_SUBTITLE, subtitle);
+ return this;
+ }
+
+ /**
+ * Optional: Set the description to display.
+ * @param description
+ * @return
+ */
+ public Builder setDescription(@NonNull CharSequence description) {
+ bundle.putCharSequence(KEY_DESCRIPTION, description);
+ return this;
+ }
+
+ /**
+ * Optional: Set the text for the positive button. If not set, the positive button
+ * will not show.
+ * @param text
+ * @return
+ * @hide
+ */
+ public Builder setPositiveButton(@NonNull CharSequence text,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull DialogInterface.OnClickListener listener) {
+ if (TextUtils.isEmpty(text)) {
+ throw new IllegalArgumentException("Text must be set and non-empty");
+ }
+ if (executor == null) {
+ throw new IllegalArgumentException("Executor must not be null");
+ }
+ if (listener == null) {
+ throw new IllegalArgumentException("Listener must not be null");
+ }
+ bundle.putCharSequence(KEY_POSITIVE_TEXT, text);
+ positiveButtonInfo = new ButtonInfo(executor, listener);
+ return this;
+ }
+
+ /**
+ * Required: Set the text for the negative button.
+ * @param text
+ * @return
+ */
+ public Builder setNegativeButton(@NonNull CharSequence text,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull DialogInterface.OnClickListener listener) {
+ if (TextUtils.isEmpty(text)) {
+ throw new IllegalArgumentException("Text must be set and non-empty");
+ }
+ if (executor == null) {
+ throw new IllegalArgumentException("Executor must not be null");
+ }
+ if (listener == null) {
+ throw new IllegalArgumentException("Listener must not be null");
+ }
+ bundle.putCharSequence(KEY_NEGATIVE_TEXT, text);
+ negativeButtonInfo = new ButtonInfo(executor, listener);
+ return this;
+ }
+
+ /**
+ * Creates a {@link FingerprintDialog} with the arguments supplied to this builder.
+ * @param context
+ * @return a {@link FingerprintDialog}
+ * @throws IllegalArgumentException if any of the required fields are not set.
+ */
+ public FingerprintDialog build(Context context) {
+ final CharSequence title = bundle.getCharSequence(KEY_TITLE);
+ final CharSequence negative = bundle.getCharSequence(KEY_NEGATIVE_TEXT);
+
+ if (TextUtils.isEmpty(title)) {
+ throw new IllegalArgumentException("Title must be set and non-empty");
+ } else if (TextUtils.isEmpty(negative)) {
+ throw new IllegalArgumentException("Negative text must be set and non-empty");
+ }
+ return new FingerprintDialog(context, bundle, positiveButtonInfo, negativeButtonInfo);
+ }
+ }
+
+ private FingerprintManager mFingerprintManager;
+ private Bundle mBundle;
+ private ButtonInfo mPositiveButtonInfo;
+ private ButtonInfo mNegativeButtonInfo;
+
+ IFingerprintDialogReceiver mDialogReceiver = new IFingerprintDialogReceiver.Stub() {
+ @Override
+ public void onDialogDismissed(int reason) {
+ // Check the reason and invoke OnClickListener(s) if necessary
+ if (reason == DISMISSED_REASON_POSITIVE) {
+ mPositiveButtonInfo.executor.execute(() -> {
+ mPositiveButtonInfo.listener.onClick(null, DialogInterface.BUTTON_POSITIVE);
+ });
+ } else if (reason == DISMISSED_REASON_NEGATIVE) {
+ mNegativeButtonInfo.executor.execute(() -> {
+ mNegativeButtonInfo.listener.onClick(null, DialogInterface.BUTTON_NEGATIVE);
+ });
+ }
+ }
+ };
+
+ private FingerprintDialog(Context context, Bundle bundle,
+ ButtonInfo positiveButtonInfo, ButtonInfo negativeButtonInfo) {
+ mBundle = bundle;
+ mPositiveButtonInfo = positiveButtonInfo;
+ mNegativeButtonInfo = negativeButtonInfo;
+ mFingerprintManager = context.getSystemService(FingerprintManager.class);
+ }
+
+ /**
+ * This call warms up the fingerprint hardware, displays a system-provided dialog,
+ * and starts scanning for a fingerprint. It terminates when
+ * {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} is called, when
+ * {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult)} is called,
+ * when {@link AuthenticationCallback#onAuthenticationFailed()} is called or when the user
+ * dismisses the system-provided dialog, at which point the crypto object becomes invalid.
+ * This operation can be canceled by using the provided cancel object. The application will
+ * receive authentication errors through {@link AuthenticationCallback}, and button events
+ * through the corresponding callback set in
+ * {@link Builder#setNegativeButton(CharSequence, Executor, DialogInterface.OnClickListener)}.
+ * It is safe to reuse the {@link FingerprintDialog} object, and calling
+ * {@link FingerprintDialog#authenticate(CancellationSignal, Executor, AuthenticationCallback)}
+ * while an existing authentication attempt is occurring will stop the previous client and
+ * start a new authentication. The interrupted client will receive a cancelled notification
+ * through {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)}.
+ *
+ * @throws IllegalArgumentException if any of the arguments are null
+ *
+ * @param crypto object associated with the call
+ * @param cancel an object that can be used to cancel authentication
+ * @param executor an executor to handle callback events
+ * @param callback an object to receive authentication events
+ */
+ @RequiresPermission(USE_FINGERPRINT)
+ public void authenticate(@NonNull FingerprintManager.CryptoObject crypto,
+ @NonNull CancellationSignal cancel,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull FingerprintManager.AuthenticationCallback callback) {
+ mFingerprintManager.authenticate(crypto, cancel, mBundle, executor, mDialogReceiver,
+ callback);
+ }
+
+ /**
+ * This call warms up the fingerprint hardware, displays a system-provided dialog,
+ * and starts scanning for a fingerprint. It terminates when
+ * {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} is called, when
+ * {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult)} is called,
+ * when {@link AuthenticationCallback#onAuthenticationFailed()} is called or when the user
+ * dismisses the system-provided dialog. This operation can be canceled by using the provided
+ * cancel object. The application will receive authentication errors through
+ * {@link AuthenticationCallback}, and button events through the corresponding callback set in
+ * {@link Builder#setNegativeButton(CharSequence, Executor, DialogInterface.OnClickListener)}.
+ * It is safe to reuse the {@link FingerprintDialog} object, and calling
+ * {@link FingerprintDialog#authenticate(CancellationSignal, Executor, AuthenticationCallback)}
+ * while an existing authentication attempt is occurring will stop the previous client and
+ * start a new authentication. The interrupted client will receive a cancelled notification
+ * through {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)}.
+ *
+ * @throws IllegalArgumentException if any of the arguments are null
+ *
+ * @param cancel an object that can be used to cancel authentication
+ * @param executor an executor to handle callback events
+ * @param callback an object to receive authentication events
+ */
+ @RequiresPermission(USE_FINGERPRINT)
+ public void authenticate(@NonNull CancellationSignal cancel,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull FingerprintManager.AuthenticationCallback callback) {
+ mFingerprintManager.authenticate(cancel, mBundle, executor, mDialogReceiver, callback);
+ }
+}
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 987718a..62d92c4 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -16,6 +16,11 @@
package android.hardware.fingerprint;
+import static android.Manifest.permission.INTERACT_ACROSS_USERS;
+import static android.Manifest.permission.MANAGE_FINGERPRINT;
+import static android.Manifest.permission.USE_FINGERPRINT;
+
+import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -23,6 +28,7 @@
import android.app.ActivityManager;
import android.content.Context;
import android.os.Binder;
+import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.CancellationSignal.OnCancelListener;
import android.os.Handler;
@@ -38,14 +44,11 @@
import java.security.Signature;
import java.util.List;
+import java.util.concurrent.Executor;
import javax.crypto.Cipher;
import javax.crypto.Mac;
-import static android.Manifest.permission.INTERACT_ACROSS_USERS;
-import static android.Manifest.permission.MANAGE_FINGERPRINT;
-import static android.Manifest.permission.USE_FINGERPRINT;
-
/**
* A class that coordinates access to the fingerprint hardware.
*/
@@ -204,6 +207,7 @@
private CryptoObject mCryptoObject;
private Fingerprint mRemovalFingerprint;
private Handler mHandler;
+ private Executor mExecutor;
private class OnEnrollCancelListener implements OnCancelListener {
@Override
@@ -505,7 +509,9 @@
}
/**
- * Per-user version
+ * Per-user version, see {@link FingerprintManager#authenticate(CryptoObject,
+ * CancellationSignal, int, AuthenticationCallback, Handler)}
+ * @param userId the user ID that the fingerprint hardware will authenticate for.
* @hide
*/
@RequiresPermission(USE_FINGERPRINT)
@@ -530,7 +536,7 @@
mCryptoObject = crypto;
long sessionId = crypto != null ? crypto.getOpId() : 0;
mService.authenticate(mToken, sessionId, userId, mServiceReceiver, flags,
- mContext.getOpPackageName());
+ mContext.getOpPackageName(), null /* bundle */, null /* receiver */);
} catch (RemoteException e) {
Log.w(TAG, "Remote exception while authenticating: ", e);
if (callback != null) {
@@ -543,6 +549,111 @@
}
/**
+ * Per-user version, see {@link FingerprintManager#authenticate(CryptoObject,
+ * CancellationSignal, Bundle, Executor, IFingerprintDialogReceiver, AuthenticationCallback)}
+ * @param userId the user ID that the fingerprint hardware will authenticate for.
+ */
+ private void authenticate(int userId,
+ @Nullable CryptoObject crypto,
+ @NonNull CancellationSignal cancel,
+ @NonNull Bundle bundle,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull IFingerprintDialogReceiver receiver,
+ @NonNull AuthenticationCallback callback) {
+ mCryptoObject = crypto;
+ if (cancel.isCanceled()) {
+ Log.w(TAG, "authentication already canceled");
+ return;
+ } else {
+ cancel.setOnCancelListener(new OnAuthenticationCancelListener(crypto));
+ }
+
+ if (mService != null) {
+ try {
+ mExecutor = executor;
+ mAuthenticationCallback = callback;
+ final long sessionId = crypto != null ? crypto.getOpId() : 0;
+ mService.authenticate(mToken, sessionId, userId, mServiceReceiver,
+ 0 /* flags */, mContext.getOpPackageName(), bundle, receiver);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Remote exception while authenticating", e);
+ mExecutor.execute(() -> {
+ callback.onAuthenticationError(FINGERPRINT_ERROR_HW_UNAVAILABLE,
+ getErrorString(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */));
+ });
+ }
+ }
+ }
+
+ /**
+ * Private method, see {@link FingerprintDialog#authenticate(CancellationSignal, Executor,
+ * AuthenticationCallback)}
+ * @param cancel
+ * @param executor
+ * @param callback
+ * @hide
+ */
+ public void authenticate(
+ @NonNull CancellationSignal cancel,
+ @NonNull Bundle bundle,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull IFingerprintDialogReceiver receiver,
+ @NonNull AuthenticationCallback callback) {
+ if (cancel == null) {
+ throw new IllegalArgumentException("Must supply a cancellation signal");
+ }
+ if (bundle == null) {
+ throw new IllegalArgumentException("Must supply a bundle");
+ }
+ if (executor == null) {
+ throw new IllegalArgumentException("Must supply an executor");
+ }
+ if (receiver == null) {
+ throw new IllegalArgumentException("Must supply a receiver");
+ }
+ if (callback == null) {
+ throw new IllegalArgumentException("Must supply a calback");
+ }
+ authenticate(UserHandle.myUserId(), null, cancel, bundle, executor, receiver, callback);
+ }
+
+ /**
+ * Private method, see {@link FingerprintDialog#authenticate(CryptoObject, CancellationSignal,
+ * Executor, AuthenticationCallback)}
+ * @param crypto
+ * @param cancel
+ * @param executor
+ * @param callback
+ * @hide
+ */
+ public void authenticate(@NonNull CryptoObject crypto,
+ @NonNull CancellationSignal cancel,
+ @NonNull Bundle bundle,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull IFingerprintDialogReceiver receiver,
+ @NonNull AuthenticationCallback callback) {
+ if (crypto == null) {
+ throw new IllegalArgumentException("Must supply a crypto object");
+ }
+ if (cancel == null) {
+ throw new IllegalArgumentException("Must supply a cancellation signal");
+ }
+ if (bundle == null) {
+ throw new IllegalArgumentException("Must supply a bundle");
+ }
+ if (executor == null) {
+ throw new IllegalArgumentException("Must supply an executor");
+ }
+ if (receiver == null) {
+ throw new IllegalArgumentException("Must supply a receiver");
+ }
+ if (callback == null) {
+ throw new IllegalArgumentException("Must supply a calback");
+ }
+ authenticate(UserHandle.myUserId(), crypto, cancel, bundle, executor, receiver, callback);
+ }
+
+ /**
* Request fingerprint enrollment. This call warms up the fingerprint hardware
* and starts scanning for fingerprints. Progress will be indicated by callbacks to the
* {@link EnrollmentCallback} object. It terminates when
@@ -929,64 +1040,64 @@
}
}
- private void sendErrorResult(long deviceId, int errMsgId, int vendorCode) {
- // emulate HAL 2.1 behavior and send real errMsgId
- final int clientErrMsgId = errMsgId == FINGERPRINT_ERROR_VENDOR
- ? (vendorCode + FINGERPRINT_ERROR_VENDOR_BASE) : errMsgId;
- if (mEnrollmentCallback != null) {
- mEnrollmentCallback.onEnrollmentError(clientErrMsgId,
- getErrorString(errMsgId, vendorCode));
- } else if (mAuthenticationCallback != null) {
- mAuthenticationCallback.onAuthenticationError(clientErrMsgId,
- getErrorString(errMsgId, vendorCode));
- } else if (mRemovalCallback != null) {
- mRemovalCallback.onRemovalError(mRemovalFingerprint, clientErrMsgId,
- getErrorString(errMsgId, vendorCode));
- } else if (mEnumerateCallback != null) {
- mEnumerateCallback.onEnumerateError(clientErrMsgId,
- getErrorString(errMsgId, vendorCode));
- }
- }
-
private void sendEnrollResult(Fingerprint fp, int remaining) {
if (mEnrollmentCallback != null) {
mEnrollmentCallback.onEnrollmentProgress(remaining);
}
}
-
- private void sendAuthenticatedSucceeded(Fingerprint fp, int userId) {
- if (mAuthenticationCallback != null) {
- final AuthenticationResult result =
- new AuthenticationResult(mCryptoObject, fp, userId);
- mAuthenticationCallback.onAuthenticationSucceeded(result);
- }
- }
-
- private void sendAuthenticatedFailed() {
- if (mAuthenticationCallback != null) {
- mAuthenticationCallback.onAuthenticationFailed();
- }
- }
-
- private void sendAcquiredResult(long deviceId, int acquireInfo, int vendorCode) {
- if (mAuthenticationCallback != null) {
- mAuthenticationCallback.onAuthenticationAcquired(acquireInfo);
- }
- final String msg = getAcquiredString(acquireInfo, vendorCode);
- if (msg == null) {
- return;
- }
- // emulate HAL 2.1 behavior and send real acquiredInfo
- final int clientInfo = acquireInfo == FINGERPRINT_ACQUIRED_VENDOR
- ? (vendorCode + FINGERPRINT_ACQUIRED_VENDOR_BASE) : acquireInfo;
- if (mEnrollmentCallback != null) {
- mEnrollmentCallback.onEnrollmentHelp(clientInfo, msg);
- } else if (mAuthenticationCallback != null) {
- mAuthenticationCallback.onAuthenticationHelp(clientInfo, msg);
- }
- }
};
+ private void sendAuthenticatedSucceeded(Fingerprint fp, int userId) {
+ if (mAuthenticationCallback != null) {
+ final AuthenticationResult result =
+ new AuthenticationResult(mCryptoObject, fp, userId);
+ mAuthenticationCallback.onAuthenticationSucceeded(result);
+ }
+ }
+
+ private void sendAuthenticatedFailed() {
+ if (mAuthenticationCallback != null) {
+ mAuthenticationCallback.onAuthenticationFailed();
+ }
+ }
+
+ private void sendAcquiredResult(long deviceId, int acquireInfo, int vendorCode) {
+ if (mAuthenticationCallback != null) {
+ mAuthenticationCallback.onAuthenticationAcquired(acquireInfo);
+ }
+ final String msg = getAcquiredString(acquireInfo, vendorCode);
+ if (msg == null) {
+ return;
+ }
+ // emulate HAL 2.1 behavior and send real acquiredInfo
+ final int clientInfo = acquireInfo == FINGERPRINT_ACQUIRED_VENDOR
+ ? (vendorCode + FINGERPRINT_ACQUIRED_VENDOR_BASE) : acquireInfo;
+ if (mEnrollmentCallback != null) {
+ mEnrollmentCallback.onEnrollmentHelp(clientInfo, msg);
+ } else if (mAuthenticationCallback != null) {
+ mAuthenticationCallback.onAuthenticationHelp(clientInfo, msg);
+ }
+ }
+
+ private void sendErrorResult(long deviceId, int errMsgId, int vendorCode) {
+ // emulate HAL 2.1 behavior and send real errMsgId
+ final int clientErrMsgId = errMsgId == FINGERPRINT_ERROR_VENDOR
+ ? (vendorCode + FINGERPRINT_ERROR_VENDOR_BASE) : errMsgId;
+ if (mEnrollmentCallback != null) {
+ mEnrollmentCallback.onEnrollmentError(clientErrMsgId,
+ getErrorString(errMsgId, vendorCode));
+ } else if (mAuthenticationCallback != null) {
+ mAuthenticationCallback.onAuthenticationError(clientErrMsgId,
+ getErrorString(errMsgId, vendorCode));
+ } else if (mRemovalCallback != null) {
+ mRemovalCallback.onRemovalError(mRemovalFingerprint, clientErrMsgId,
+ getErrorString(errMsgId, vendorCode));
+ } else if (mEnumerateCallback != null) {
+ mEnumerateCallback.onEnumerateError(clientErrMsgId,
+ getErrorString(errMsgId, vendorCode));
+ }
+ }
+
/**
* @hide
*/
@@ -1023,7 +1134,10 @@
}
}
- private String getErrorString(int errMsg, int vendorCode) {
+ /**
+ * @hide
+ */
+ public String getErrorString(int errMsg, int vendorCode) {
switch (errMsg) {
case FINGERPRINT_ERROR_UNABLE_TO_PROCESS:
return mContext.getString(
@@ -1043,6 +1157,9 @@
case FINGERPRINT_ERROR_LOCKOUT_PERMANENT:
return mContext.getString(
com.android.internal.R.string.fingerprint_error_lockout_permanent);
+ case FINGERPRINT_ERROR_USER_CANCELED:
+ return mContext.getString(
+ com.android.internal.R.string.fingerprint_error_user_canceled);
case FINGERPRINT_ERROR_VENDOR: {
String[] msgArray = mContext.getResources().getStringArray(
com.android.internal.R.array.fingerprint_error_vendor);
@@ -1055,7 +1172,10 @@
return null;
}
- private String getAcquiredString(int acquireInfo, int vendorCode) {
+ /**
+ * @hide
+ */
+ public String getAcquiredString(int acquireInfo, int vendorCode) {
switch (acquireInfo) {
case FINGERPRINT_ACQUIRED_GOOD:
return null;
@@ -1096,22 +1216,47 @@
@Override // binder call
public void onAcquired(long deviceId, int acquireInfo, int vendorCode) {
- mHandler.obtainMessage(MSG_ACQUIRED, acquireInfo, vendorCode, deviceId).sendToTarget();
+ if (mExecutor != null) {
+ mExecutor.execute(() -> {
+ sendAcquiredResult(deviceId, acquireInfo, vendorCode);
+ });
+ } else {
+ mHandler.obtainMessage(MSG_ACQUIRED, acquireInfo, vendorCode,
+ deviceId).sendToTarget();
+ }
}
@Override // binder call
public void onAuthenticationSucceeded(long deviceId, Fingerprint fp, int userId) {
- mHandler.obtainMessage(MSG_AUTHENTICATION_SUCCEEDED, userId, 0, fp).sendToTarget();
+ if (mExecutor != null) {
+ mExecutor.execute(() -> {
+ sendAuthenticatedSucceeded(fp, userId);
+ });
+ } else {
+ mHandler.obtainMessage(MSG_AUTHENTICATION_SUCCEEDED, userId, 0, fp).sendToTarget();
+ }
}
@Override // binder call
public void onAuthenticationFailed(long deviceId) {
- mHandler.obtainMessage(MSG_AUTHENTICATION_FAILED).sendToTarget();
+ if (mExecutor != null) {
+ mExecutor.execute(() -> {
+ sendAuthenticatedFailed();
+ });
+ } else {
+ mHandler.obtainMessage(MSG_AUTHENTICATION_FAILED).sendToTarget();
+ }
}
@Override // binder call
public void onError(long deviceId, int error, int vendorCode) {
- mHandler.obtainMessage(MSG_ERROR, error, vendorCode, deviceId).sendToTarget();
+ if (mExecutor != null) {
+ mExecutor.execute(() -> {
+ sendErrorResult(deviceId, error, vendorCode);
+ });
+ } else {
+ mHandler.obtainMessage(MSG_ERROR, error, vendorCode, deviceId).sendToTarget();
+ }
}
@Override // binder call
diff --git a/core/java/android/hardware/fingerprint/IFingerprintDialogReceiver.aidl b/core/java/android/hardware/fingerprint/IFingerprintDialogReceiver.aidl
new file mode 100644
index 0000000..13e7974
--- /dev/null
+++ b/core/java/android/hardware/fingerprint/IFingerprintDialogReceiver.aidl
@@ -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 android.hardware.fingerprint;
+
+import android.hardware.fingerprint.Fingerprint;
+import android.os.Bundle;
+import android.os.UserHandle;
+
+/**
+ * Communication channel from the FingerprintDialog (SysUI) back to AuthenticationClient.
+ * @hide
+ */
+oneway interface IFingerprintDialogReceiver {
+ void onDialogDismissed(int reason);
+}
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index 4879d54..f1502e4 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -17,6 +17,7 @@
import android.os.Bundle;
import android.hardware.fingerprint.IFingerprintClientActiveCallback;
+import android.hardware.fingerprint.IFingerprintDialogReceiver;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
import android.hardware.fingerprint.IFingerprintServiceLockoutResetCallback;
import android.hardware.fingerprint.Fingerprint;
@@ -29,7 +30,8 @@
interface IFingerprintService {
// Authenticate the given sessionId with a fingerprint
void authenticate(IBinder token, long sessionId, int userId,
- IFingerprintServiceReceiver receiver, int flags, String opPackageName);
+ IFingerprintServiceReceiver receiver, int flags, String opPackageName,
+ in Bundle bundle, IFingerprintDialogReceiver dialogReceiver);
// Cancel authentication for the given sessionId
void cancelAuthentication(IBinder token, String opPackageName);
diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java
index f04f03f6..6125394 100644
--- a/core/java/android/net/IpSecManager.java
+++ b/core/java/android/net/IpSecManager.java
@@ -748,7 +748,7 @@
* @hide
*/
@SystemApi
- void applyTunnelModeTransform(IpSecTunnelInterface tunnel, int direction,
+ public void applyTunnelModeTransform(IpSecTunnelInterface tunnel, int direction,
IpSecTransform transform) throws IOException {
// TODO: call IpSecService
}
diff --git a/core/java/android/os/Handler.java b/core/java/android/os/Handler.java
index 5c5e351..fc88e90 100644
--- a/core/java/android/os/Handler.java
+++ b/core/java/android/os/Handler.java
@@ -202,7 +202,8 @@
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
- "Can't create handler inside thread that has not called Looper.prepare()");
+ "Can't create handler inside thread " + Thread.currentThread()
+ + " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 3798a5e..61172e1 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -23,6 +23,7 @@
import android.annotation.SystemService;
import android.content.Context;
import android.util.Log;
+import android.util.proto.ProtoOutputStream;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -1652,6 +1653,21 @@
}
}
+ /** @hide */
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ synchronized (mToken) {
+ final long token = proto.start(fieldId);
+ proto.write(PowerManagerProto.WakeLockProto.HEX_STRING,
+ Integer.toHexString(System.identityHashCode(this)));
+ proto.write(PowerManagerProto.WakeLockProto.HELD, mHeld);
+ proto.write(PowerManagerProto.WakeLockProto.INTERNAL_COUNT, mInternalCount);
+ if (mWorkSource != null) {
+ mWorkSource.writeToProto(proto, PowerManagerProto.WakeLockProto.WORK_SOURCE);
+ }
+ proto.end(token);
+ }
+ }
+
/**
* Wraps a Runnable such that this method immediately acquires the wake lock and then
* once the Runnable is done the wake lock is released.
diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java
index 3ef0961..c7d89b0 100644
--- a/core/java/android/os/PowerManagerInternal.java
+++ b/core/java/android/os/PowerManagerInternal.java
@@ -71,6 +71,24 @@
}
/**
+ * Converts platform constants to proto enums.
+ */
+ public static int wakefulnessToProtoEnum(int wakefulness) {
+ switch (wakefulness) {
+ case WAKEFULNESS_ASLEEP:
+ return PowerManagerInternalProto.WAKEFULNESS_ASLEEP;
+ case WAKEFULNESS_AWAKE:
+ return PowerManagerInternalProto.WAKEFULNESS_AWAKE;
+ case WAKEFULNESS_DREAMING:
+ return PowerManagerInternalProto.WAKEFULNESS_DREAMING;
+ case WAKEFULNESS_DOZING:
+ return PowerManagerInternalProto.WAKEFULNESS_DOZING;
+ default:
+ return wakefulness;
+ }
+ }
+
+ /**
* Returns true if the wakefulness state represents an interactive state
* as defined by {@link android.os.PowerManager#isInteractive}.
*/
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 7683880..6833908 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -574,6 +574,14 @@
}
/**
+ * Returns whether the given uid belongs to a system core component or not.
+ * @hide
+ */
+ public static boolean isCoreUid(int uid) {
+ return UserHandle.isCore(uid);
+ }
+
+ /**
* Returns whether the given uid belongs to an application.
* @param uid A kernel uid.
* @return Whether the uid corresponds to an application sandbox running in
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index 6381b56..5be72bc 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -126,7 +126,10 @@
return getAppId(uid1) == getAppId(uid2);
}
- /** @hide */
+ /**
+ * Whether a UID is an "isolated" UID.
+ * @hide
+ */
public static boolean isIsolated(int uid) {
if (uid > 0) {
final int appId = getAppId(uid);
@@ -136,7 +139,11 @@
}
}
- /** @hide */
+ /**
+ * Whether a UID belongs to a regular app. *Note* "Not a regular app" does not mean
+ * "it's system", because of isolated UIDs. Use {@link #isCore} for that.
+ * @hide
+ */
public static boolean isApp(int uid) {
if (uid > 0) {
final int appId = getAppId(uid);
@@ -147,6 +154,19 @@
}
/**
+ * Whether a UID belongs to a system core component or not.
+ * @hide
+ */
+ public static boolean isCore(int uid) {
+ if (uid > 0) {
+ final int appId = getAppId(uid);
+ return appId < Process.FIRST_APPLICATION_UID;
+ } else {
+ return false;
+ }
+ }
+
+ /**
* Returns the user for a given uid.
* @param uid A uid for an application running in a particular user.
* @return A {@link UserHandle} for that user.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 1c41979..b5f23be 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1479,6 +1479,21 @@
public static final String ACTION_REQUEST_SET_AUTOFILL_SERVICE =
"android.settings.REQUEST_SET_AUTOFILL_SERVICE";
+ /**
+ * Activity Action: Show screen for controlling which apps have access on volume directories.
+ * <p>
+ * Input: Nothing.
+ * <p>
+ * Output: Nothing.
+ * <p>
+ * Applications typically use this action to ask the user to revert the "Do not ask again"
+ * status of directory access requests made by
+ * {@link android.os.storage.StorageVolume#createAccessIntent(String)}.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_STORAGE_VOLUME_ACCESS_SETTINGS =
+ "android.settings.STORAGE_VOLUME_ACCESS_SETTINGS";
+
// End of Intent actions for Settings
/**
@@ -11159,6 +11174,7 @@
*
* @hide
*/
+ @TestApi
public static final String LOCATION_GLOBAL_KILL_SWITCH =
"location_global_kill_switch";
diff --git a/core/java/android/service/autofill/AutofillFieldClassificationService.java b/core/java/android/service/autofill/AutofillFieldClassificationService.java
index df63a91..1ef6100 100644
--- a/core/java/android/service/autofill/AutofillFieldClassificationService.java
+++ b/core/java/android/service/autofill/AutofillFieldClassificationService.java
@@ -172,6 +172,7 @@
* {@hide}
*/
public static final class Scores implements Parcelable {
+ @NonNull
public final float[][] scores;
private Scores(Parcel parcel) {
@@ -185,11 +186,23 @@
}
}
- private Scores(float[][] scores) {
+ private Scores(@NonNull float[][] scores) {
this.scores = scores;
}
@Override
+ public String toString() {
+ final int size1 = scores.length;
+ final int size2 = size1 > 0 ? scores[0].length : 0;
+ final StringBuilder builder = new StringBuilder("Scores [")
+ .append(size1).append("x").append(size2).append("] ");
+ for (int i = 0; i < size1; i++) {
+ builder.append(i).append(": ").append(Arrays.toString(scores[i])).append(' ');
+ }
+ return builder.toString();
+ }
+
+ @Override
public int describeContents() {
return 0;
}
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 9f9033c..b313514 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -40,7 +40,7 @@
DEFAULT_FLAGS.put("device_info_v2", "true");
DEFAULT_FLAGS.put("settings_app_info_v2", "true");
DEFAULT_FLAGS.put("settings_connected_device_v2", "true");
- DEFAULT_FLAGS.put("settings_battery_v2", "false");
+ DEFAULT_FLAGS.put("settings_battery_v2", "true");
DEFAULT_FLAGS.put("settings_battery_display_app_list", "false");
DEFAULT_FLAGS.put("settings_security_settings_v2", "true");
DEFAULT_FLAGS.put("settings_zone_picker_v2", "true");
diff --git a/core/java/android/util/PackageUtils.java b/core/java/android/util/PackageUtils.java
index e2e9d53..a5e3818 100644
--- a/core/java/android/util/PackageUtils.java
+++ b/core/java/android/util/PackageUtils.java
@@ -105,7 +105,7 @@
* @param data The data.
* @return The digest or null if an error occurs.
*/
- public static @Nullable String computeSha256Digest(@NonNull byte[] data) {
+ public static @Nullable byte[] computeSha256DigestBytes(@NonNull byte[] data) {
MessageDigest messageDigest;
try {
messageDigest = MessageDigest.getInstance("SHA256");
@@ -116,6 +116,15 @@
messageDigest.update(data);
- return ByteStringUtils.toHexString(messageDigest.digest());
+ return messageDigest.digest();
+ }
+
+ /**
+ * Computes the SHA256 digest of some data.
+ * @param data The data.
+ * @return The digest or null if an error occurs.
+ */
+ public static @Nullable String computeSha256Digest(@NonNull byte[] data) {
+ return ByteStringUtils.toHexString(computeSha256DigestBytes(data));
}
}
diff --git a/core/java/android/util/proto/ProtoUtils.java b/core/java/android/util/proto/ProtoUtils.java
index 85b7ec8..c7bbb9f 100644
--- a/core/java/android/util/proto/ProtoUtils.java
+++ b/core/java/android/util/proto/ProtoUtils.java
@@ -48,4 +48,26 @@
proto.write(Duration.END_MS, endMs);
proto.end(token);
}
+
+ /**
+ * Helper function to write bit-wise flags to proto as repeated enums
+ * @hide
+ */
+ public static void writeBitWiseFlagsToProtoEnum(ProtoOutputStream proto, long fieldId,
+ int flags, int[] origEnums, int[] protoEnums) {
+ if (protoEnums.length != origEnums.length) {
+ throw new IllegalArgumentException("The length of origEnums must match protoEnums");
+ }
+ int len = origEnums.length;
+ for (int i = 0; i < len; i++) {
+ // handle zero flag case.
+ if (origEnums[i] == 0 && flags == 0) {
+ proto.write(fieldId, protoEnums[i]);
+ return;
+ }
+ if ((flags & origEnums[i]) != 0) {
+ proto.write(fieldId, protoEnums[i]);
+ }
+ }
+ }
}
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index ed2b8b6..a597405 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -806,8 +806,10 @@
public static final int KEYCODE_SYSTEM_NAVIGATION_RIGHT = 283;
/** Key code constant: Show all apps */
public static final int KEYCODE_ALL_APPS = 284;
+ /** Key code constant: Refresh key. */
+ public static final int KEYCODE_REFRESH = 285;
- private static final int LAST_KEYCODE = KEYCODE_ALL_APPS;
+ private static final int LAST_KEYCODE = KEYCODE_REFRESH;
// NOTE: If you add a new keycode here you must also add it to:
// isSystem()
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index e0864bd..4631261 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -57,6 +57,7 @@
import android.view.animation.AnimationUtils;
import android.view.animation.LayoutAnimationController;
import android.view.animation.Transformation;
+import android.view.autofill.Helper;
import com.android.internal.R;
@@ -3474,8 +3475,10 @@
}
if (!isLaidOut()) {
- Log.v(VIEW_LOG_TAG, "dispatchProvideStructure(): not laid out, ignoring "
- + childrenCount + " children of " + getAccessibilityViewId());
+ if (Helper.sVerbose) {
+ Log.v(VIEW_LOG_TAG, "dispatchProvideStructure(): not laid out, ignoring "
+ + childrenCount + " children of " + getAccessibilityViewId());
+ }
return;
}
diff --git a/core/java/android/widget/MediaControlView2.java b/core/java/android/widget/MediaControlView2.java
index 0aa2b64..42ba61b 100644
--- a/core/java/android/widget/MediaControlView2.java
+++ b/core/java/android/widget/MediaControlView2.java
@@ -28,6 +28,21 @@
import android.view.MotionEvent;
/**
+ * A View that contains the controls for MediaPlayer2.
+ * It provides a wide range of UI including buttons such as "Play/Pause", "Rewind", "Fast Forward",
+ * "Subtitle", "Full Screen", and it is also possible to add multiple custom buttons.
+ *
+ * <p>
+ * <em> MediaControlView2 can be initialized in two different ways: </em>
+ * 1) When VideoView2 is initialized, it automatically initializes a MediaControlView2 instance and
+ * adds it to the view.
+ * 2) Initialize MediaControlView2 programmatically and add it to a ViewGroup instance.
+ *
+ * In the first option, VideoView2 automatically connects MediaControlView2 to MediaController2,
+ * which is necessary to communicate with MediaSession2. In the second option, however, the
+ * developer needs to manually retrieve a MediaController2 instance and set it to MediaControlView2
+ * by calling setController(MediaController2 controller).
+ *
* TODO PUBLIC API
* @hide
*/
@@ -55,103 +70,103 @@
.createMediaControlView2(this, new SuperProvider());
}
+ /**
+ * @hide
+ */
public MediaControlView2Provider getProvider() {
return mProvider;
}
/**
- * TODO: add docs
+ * Sets MediaController2 instance to control corresponding MediaSession2.
*/
public void setController(MediaController controller) {
mProvider.setController_impl(controller);
}
/**
- * TODO: add docs
+ * Shows the control view on screen. It will disappear automatically after 3 seconds of
+ * inactivity.
*/
public void show() {
mProvider.show_impl();
}
/**
- * TODO: add docs
+ * Shows the control view on screen. It will disappear automatically after {@code timeout}
+ * milliseconds of inactivity.
*/
public void show(int timeout) {
mProvider.show_impl(timeout);
}
/**
- * TODO: add docs
+ * Returns whether the control view is currently shown or hidden.
*/
public boolean isShowing() {
return mProvider.isShowing_impl();
}
/**
- * TODO: add docs
+ * Hide the control view from the screen.
*/
public void hide() {
mProvider.hide_impl();
}
/**
- * TODO: add docs
- */
- public void showCCButton() {
- mProvider.showCCButton_impl();
- }
-
- /**
- * TODO: add docs
+ * Returns whether the media is currently playing or not.
*/
public boolean isPlaying() {
return mProvider.isPlaying_impl();
}
/**
- * TODO: add docs
+ * Returns the current position of the media in milliseconds.
*/
public int getCurrentPosition() {
return mProvider.getCurrentPosition_impl();
}
/**
- * TODO: add docs
+ * Returns the percentage of how much of the media is currently buffered in storage.
*/
public int getBufferPercentage() {
return mProvider.getBufferPercentage_impl();
}
/**
- * TODO: add docs
+ * Returns whether the media can be paused or not.
*/
public boolean canPause() {
return mProvider.canPause_impl();
}
/**
- * TODO: add docs
+ * Returns whether the media can be rewound or not.
*/
public boolean canSeekBackward() {
return mProvider.canSeekBackward_impl();
}
/**
- * TODO: add docs
+ * Returns whether the media can be fast-forwarded or not.
*/
public boolean canSeekForward() {
return mProvider.canSeekForward_impl();
}
/**
- * TODO: add docs
+ * If the media selected has a subtitle track, calling this method will display the subtitle at
+ * the bottom of the view. If a media has multiple subtitle tracks, this method will select the
+ * first one of them.
*/
public void showSubtitle() {
mProvider.showSubtitle_impl();
}
/**
- * TODO: add docs
+ * Hides the currently displayed subtitle.
*/
public void hideSubtitle() {
mProvider.hideSubtitle_impl();
diff --git a/core/java/com/android/internal/net/NetworkStatsFactory.java b/core/java/com/android/internal/net/NetworkStatsFactory.java
index 02cd09f..43abade 100644
--- a/core/java/com/android/internal/net/NetworkStatsFactory.java
+++ b/core/java/com/android/internal/net/NetworkStatsFactory.java
@@ -31,13 +31,17 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.ProcFileReader;
+import com.google.android.collect.Lists;
import libcore.io.IoUtils;
+import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
+import java.io.FileReader;
import java.io.IOException;
import java.net.ProtocolException;
+import java.util.ArrayList;
import java.util.Objects;
/**
@@ -57,6 +61,8 @@
// Used for correct stats accounting on clatd interfaces.
private static final int IPV4V6_HEADER_DELTA = 20;
+ /** Path to {@code /proc/net/dev}. */
+ private final File mStatsIfaceDev;
/** Path to {@code /proc/net/xt_qtaguid/iface_stat_all}. */
private final File mStatsXtIfaceAll;
/** Path to {@code /proc/net/xt_qtaguid/iface_stat_fmt}. */
@@ -64,6 +70,8 @@
/** Path to {@code /proc/net/xt_qtaguid/stats}. */
private final File mStatsXtUid;
+ private boolean mUseBpfStats;
+
// TODO: to improve testability and avoid global state, do not use a static variable.
@GuardedBy("sStackedIfaces")
private static final ArrayMap<String, String> sStackedIfaces = new ArrayMap<>();
@@ -79,14 +87,54 @@
}
public NetworkStatsFactory() {
- this(new File("/proc/"));
+ this(new File("/proc/"), new File("/sys/fs/bpf/traffic_uid_stats_map").exists());
}
@VisibleForTesting
- public NetworkStatsFactory(File procRoot) {
+ public NetworkStatsFactory(File procRoot, boolean useBpfStats) {
+ mStatsIfaceDev = new File(procRoot, "net/dev");
mStatsXtIfaceAll = new File(procRoot, "net/xt_qtaguid/iface_stat_all");
mStatsXtIfaceFmt = new File(procRoot, "net/xt_qtaguid/iface_stat_fmt");
mStatsXtUid = new File(procRoot, "net/xt_qtaguid/stats");
+ mUseBpfStats = useBpfStats;
+ }
+
+ @VisibleForTesting
+ public NetworkStats readNetworkStatsIfaceDev() throws IOException {
+ final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
+
+ final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 6);
+ final NetworkStats.Entry entry = new NetworkStats.Entry();
+
+ BufferedReader reader = null;
+ try {
+ reader = new BufferedReader(new FileReader(mStatsIfaceDev));
+
+ // skip first two header lines
+ reader.readLine();
+ reader.readLine();
+
+ // parse remaining lines
+ String line;
+ while ((line = reader.readLine()) != null) {
+ String[] values = line.trim().split("\\:?\\s+");
+ entry.iface = values[0];
+ entry.uid = UID_ALL;
+ entry.set = SET_ALL;
+ entry.tag = TAG_NONE;
+ entry.rxBytes = Long.parseLong(values[1]);
+ entry.rxPackets = Long.parseLong(values[2]);
+ entry.txBytes = Long.parseLong(values[9]);
+ entry.txPackets = Long.parseLong(values[10]);
+ stats.addValues(entry);
+ }
+ } catch (NullPointerException|NumberFormatException e) {
+ throw new ProtocolException("problem parsing stats", e);
+ } finally {
+ IoUtils.closeQuietly(reader);
+ StrictMode.setThreadPolicy(savedPolicy);
+ }
+ return stats;
}
/**
@@ -98,6 +146,11 @@
* @throws IllegalStateException when problem parsing stats.
*/
public NetworkStats readNetworkStatsSummaryDev() throws IOException {
+
+ // Return the stats get from /proc/net/dev if switched to bpf module.
+ if (mUseBpfStats)
+ return readNetworkStatsIfaceDev();
+
final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 6);
@@ -149,6 +202,11 @@
* @throws IllegalStateException when problem parsing stats.
*/
public NetworkStats readNetworkStatsSummaryXt() throws IOException {
+
+ // Return the stats get from /proc/net/dev if qtaguid module is replaced.
+ if (mUseBpfStats)
+ return readNetworkStatsIfaceDev();
+
final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
// return null when kernel doesn't support
@@ -254,7 +312,7 @@
stats = new NetworkStats(SystemClock.elapsedRealtime(), -1);
}
if (nativeReadNetworkStatsDetail(stats, mStatsXtUid.getAbsolutePath(), limitUid,
- limitIfaces, limitTag) != 0) {
+ limitIfaces, limitTag, mUseBpfStats) != 0) {
throw new IOException("Failed to parse network stats");
}
if (SANITY_CHECK_NATIVE) {
@@ -348,6 +406,6 @@
* are expected to monotonically increase since device boot.
*/
@VisibleForTesting
- public static native int nativeReadNetworkStatsDetail(
- NetworkStats stats, String path, int limitUid, String[] limitIfaces, int limitTag);
+ public static native int nativeReadNetworkStatsDetail(NetworkStats stats, String path,
+ int limitUid, String[] limitIfaces, int limitTag, boolean useBpfStats);
}
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index e097362a..f5af80a 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -18,6 +18,7 @@
import android.content.ComponentName;
import android.graphics.Rect;
+import android.hardware.fingerprint.IFingerprintDialogReceiver;
import android.os.Bundle;
import android.service.notification.StatusBarNotification;
@@ -130,4 +131,15 @@
void handleSystemKey(in int key);
void showShutdownUi(boolean isReboot, String reason);
+
+ // Used to show the dialog when FingerprintService starts authentication
+ void showFingerprintDialog(in Bundle bundle, IFingerprintDialogReceiver receiver);
+ // Used to hide the dialog when a finger is authenticated
+ void onFingerprintAuthenticated();
+ // Used to set a temporary message, e.g. fingerprint not recognized, finger moved too fast, etc
+ void onFingerprintHelp(String message);
+ // Used to set a message - the dialog will dismiss after a certain amount of time
+ void onFingerprintError(String error);
+ // Used to hide the fingerprint dialog when the authenticationclient is stopped
+ void hideFingerprintDialog();
}
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 03603e4..cb0b53c 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -20,6 +20,7 @@
import android.graphics.Rect;
import android.os.Bundle;
import android.service.notification.StatusBarNotification;
+import android.hardware.fingerprint.IFingerprintDialogReceiver;
import com.android.internal.statusbar.IStatusBar;
import com.android.internal.statusbar.StatusBarIcon;
@@ -79,4 +80,15 @@
void remTile(in ComponentName tile);
void clickTile(in ComponentName tile);
void handleSystemKey(in int key);
+
+ // Used to show the dialog when FingerprintService starts authentication
+ void showFingerprintDialog(in Bundle bundle, IFingerprintDialogReceiver receiver);
+ // Used to hide the dialog when a finger is authenticated
+ void onFingerprintAuthenticated();
+ // Used to set a temporary message, e.g. fingerprint not recognized, finger moved too fast, etc
+ void onFingerprintHelp(String message);
+ // Used to set a message - the dialog will dismiss after a certain amount of time
+ void onFingerprintError(String error);
+ // Used to hide the fingerprint dialog when the authenticationclient is stopped
+ void hideFingerprintDialog();
}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 543acc7..47765d9 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -228,6 +228,8 @@
],
shared_libs: [
+ "libbpf",
+ "libnetdutils",
"libmemtrack",
"libandroidfw",
"libappfuse",
diff --git a/core/jni/com_android_internal_net_NetworkStatsFactory.cpp b/core/jni/com_android_internal_net_NetworkStatsFactory.cpp
index d254de6..99d9839 100644
--- a/core/jni/com_android_internal_net_NetworkStatsFactory.cpp
+++ b/core/jni/com_android_internal_net_NetworkStatsFactory.cpp
@@ -30,7 +30,14 @@
#include <utils/Log.h>
#include <utils/misc.h>
-#include <utils/Vector.h>
+
+#include "android-base/unique_fd.h"
+#include "bpf/BpfNetworkStats.h"
+#include "bpf/BpfUtils.h"
+
+using android::bpf::hasBpfSupport;
+using android::bpf::parseBpfNetworkStatsDetail;
+using android::bpf::stats_line;
namespace android {
@@ -53,17 +60,6 @@
jfieldID operations;
} gNetworkStatsClassInfo;
-struct stats_line {
- char iface[32];
- int32_t uid;
- int32_t set;
- int32_t tag;
- int64_t rxBytes;
- int64_t rxPackets;
- int64_t txBytes;
- int64_t txPackets;
-};
-
static jobjectArray get_string_array(JNIEnv* env, jobject obj, jfieldID field, int size, bool grow)
{
if (!grow) {
@@ -97,33 +93,14 @@
return env->NewLongArray(size);
}
-static int readNetworkStatsDetail(JNIEnv* env, jclass clazz, jobject stats,
- jstring path, jint limitUid, jobjectArray limitIfacesObj, jint limitTag) {
- ScopedUtfChars path8(env, path);
- if (path8.c_str() == NULL) {
- return -1;
- }
-
- FILE *fp = fopen(path8.c_str(), "r");
+static int legacyReadNetworkStatsDetail(std::vector<stats_line>* lines,
+ const std::vector<std::string>& limitIfaces,
+ int limitTag, int limitUid, const char* path) {
+ FILE* fp = fopen(path, "r");
if (fp == NULL) {
return -1;
}
- Vector<String8> limitIfaces;
- if (limitIfacesObj != NULL && env->GetArrayLength(limitIfacesObj) > 0) {
- int num = env->GetArrayLength(limitIfacesObj);
- limitIfaces.setCapacity(num);
- for (int i=0; i<num; i++) {
- jstring string = (jstring)env->GetObjectArrayElement(limitIfacesObj, i);
- ScopedUtfChars string8(env, string);
- if (string8.c_str() != NULL) {
- limitIfaces.add(String8(string8.c_str()));
- }
- }
- }
-
- Vector<stats_line> lines;
-
int lastIdx = 1;
int idx;
char buffer[384];
@@ -215,7 +192,7 @@
//ALOGI("skipping due to uid: %s", buffer);
continue;
}
- lines.push_back(s);
+ lines->push_back(s);
} else {
//ALOGI("skipping due to bad remaining fields: %s", pos);
}
@@ -225,8 +202,42 @@
ALOGE("Failed to close netstats file");
return -1;
}
+ return 0;
+}
+
+static int readNetworkStatsDetail(JNIEnv* env, jclass clazz, jobject stats, jstring path,
+ jint limitUid, jobjectArray limitIfacesObj, jint limitTag,
+ jboolean useBpfStats) {
+ ScopedUtfChars path8(env, path);
+ if (path8.c_str() == NULL) {
+ return -1;
+ }
+
+ std::vector<std::string> limitIfaces;
+ if (limitIfacesObj != NULL && env->GetArrayLength(limitIfacesObj) > 0) {
+ int num = env->GetArrayLength(limitIfacesObj);
+ for (int i = 0; i < num; i++) {
+ jstring string = (jstring)env->GetObjectArrayElement(limitIfacesObj, i);
+ ScopedUtfChars string8(env, string);
+ if (string8.c_str() != NULL) {
+ limitIfaces.push_back(std::string(string8.c_str()));
+ }
+ }
+ }
+ std::vector<stats_line> lines;
+
+
+ if (useBpfStats) {
+ if (parseBpfNetworkStatsDetail(&lines, limitIfaces, limitTag, limitUid) < 0)
+ return -1;
+ } else {
+ if (legacyReadNetworkStatsDetail(&lines, limitIfaces, limitTag,
+ limitUid, path8.c_str()) < 0)
+ return -1;
+ }
int size = lines.size();
+
bool grow = size > env->GetIntField(stats, gNetworkStatsClassInfo.capacity);
ScopedLocalRef<jobjectArray> iface(env, get_string_array(env, stats,
@@ -303,7 +314,7 @@
static const JNINativeMethod gMethods[] = {
{ "nativeReadNetworkStatsDetail",
- "(Landroid/net/NetworkStats;Ljava/lang/String;I[Ljava/lang/String;I)I",
+ "(Landroid/net/NetworkStats;Ljava/lang/String;I[Ljava/lang/String;IZ)I",
(void*) readNetworkStatsDetail }
};
diff --git a/core/proto/android/app/activitymanager.proto b/core/proto/android/app/activitymanager.proto
index 3412a32..03f8204 100644
--- a/core/proto/android/app/activitymanager.proto
+++ b/core/proto/android/app/activitymanager.proto
@@ -19,6 +19,7 @@
package android.app;
option java_multiple_files = true;
+option java_outer_classname = "ActivityManagerProto";
// ActivityManager.java PROCESS_STATEs
enum ProcessState {
@@ -79,3 +80,16 @@
PROCESS_STATE_NONEXISTENT = 1900;
}
+// ActivityManager.java UID_OBSERVERs flags
+enum UidObserverFlag {
+ // report changes in process state, original value is 1 << 0
+ UID_OBSERVER_FLAG_PROCSTATE = 1;
+ // report uid gone, original value is 1 << 1
+ UID_OBSERVER_FLAG_GONE = 2;
+ // report uid has become idle, original value is 1 << 2
+ UID_OBSERVER_FLAG_IDLE = 3;
+ // report uid has become active, original value is 1 << 3
+ UID_OBSERVER_FLAG_ACTIVE = 4;
+ // report uid cached state has changed, original value is 1 << 4
+ UID_OBSERVER_FLAG_CACHED = 5;
+}
diff --git a/core/proto/android/app/profilerinfo.proto b/core/proto/android/app/profilerinfo.proto
new file mode 100644
index 0000000..ca1b935
--- /dev/null
+++ b/core/proto/android/app/profilerinfo.proto
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+option java_package = "android.app";
+option java_multiple_files = true;
+
+package android.app;
+
+/**
+ * An android.app.ProfilerInfo object.
+ */
+message ProfilerInfoProto {
+ optional string profile_file = 1;
+ optional int32 profile_fd = 2;
+ optional int32 sampling_interval = 3;
+ optional bool auto_stop_profiler = 4;
+ optional bool streaming_output = 5;
+ optional string agent = 6;
+}
diff --git a/core/proto/android/content/package_item_info.proto b/core/proto/android/content/package_item_info.proto
new file mode 100644
index 0000000..8470159
--- /dev/null
+++ b/core/proto/android/content/package_item_info.proto
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+option java_package = "android.content.pm";
+option java_multiple_files = true;
+
+package android.content.pm;
+
+message PackageItemInfoProto {
+ optional string name = 1;
+ optional string package_name = 2;
+ optional int32 label_res = 3;
+ optional string non_localized_label = 4;
+ optional int32 icon = 5;
+ optional int32 banner = 6;
+}
+
+// Proto of android.content.pm.ApplicationInfo which extends PackageItemInfo
+message ApplicationInfoProto {
+ optional PackageItemInfoProto package = 1;
+ optional string permission = 2;
+ optional string process_name = 3;
+ optional int32 uid = 4;
+ optional int32 flags = 5;
+ optional int32 private_flags = 6;
+ optional int32 theme = 7;
+ optional string source_dir = 8;
+ optional string public_source_dir = 9;
+ repeated string split_source_dirs = 10;
+ repeated string split_public_source_dirs = 11;
+ repeated string resource_dirs = 12;
+ optional string data_dir = 13;
+ optional string class_loader_name = 14;
+ repeated string split_class_loader_names = 15;
+
+ message Version {
+ optional bool enabled = 1;
+ optional int32 min_sdk_version = 2;
+ optional int32 target_sdk_version = 3;
+ optional int32 version_code = 4;
+ optional int32 target_sandbox_version = 5;
+ }
+ optional Version version = 16;
+
+ message Detail {
+ optional string class_name = 1;
+ optional string task_affinity = 2;
+ optional int32 requires_smallest_width_dp = 3;
+ optional int32 compatible_width_limit_dp = 4;
+ optional int32 largest_width_limit_dp = 5;
+ optional string seinfo = 6;
+ optional string seinfo_user = 7;
+ optional string device_protected_data_dir = 8;
+ optional string credential_protected_data_dir = 9;
+ repeated string shared_library_files = 10;
+ optional string manage_space_activity_name = 11;
+ optional int32 description_res = 12;
+ optional int32 ui_options = 13;
+ optional bool supports_rtl = 14;
+ oneof full_backup_content {
+ string content = 15;
+ bool is_full_backup = 16;
+ }
+ optional int32 networkSecurity_config_res = 17;
+ optional int32 category = 18;
+ }
+ optional Detail detail = 17;
+}
diff --git a/core/proto/android/os/batterystats.proto b/core/proto/android/os/batterystats.proto
index 331f80f..ce1d5c9 100644
--- a/core/proto/android/os/batterystats.proto
+++ b/core/proto/android/os/batterystats.proto
@@ -22,8 +22,11 @@
import "frameworks/base/core/proto/android/app/jobparameters.proto";
import "frameworks/base/core/proto/android/os/powermanager.proto";
import "frameworks/base/core/proto/android/telephony/signalstrength.proto";
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
message BatteryStatsProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
optional int32 report_version = 1;
optional int64 parcel_version = 2;
optional string start_platform_version = 3;
@@ -33,6 +36,8 @@
}
message ControllerActivityProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
// Time (milliseconds) spent in the idle state.
optional int64 idle_duration_ms = 1;
// Time (milliseconds) spent in the receive state.
@@ -45,6 +50,8 @@
// of power. The levels themselves are controller-specific (and may possibly
// be device specific...yet to be confirmed).
message TxLevel {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
// Transmit level. Higher levels draw more power.
optional int32 level = 1;
// Time spent in this specific transmit level state.
@@ -54,7 +61,11 @@
}
message SystemProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
message Battery {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
// Wall clock time when the data collection started.
// In case of device time manually reset by users:
// start_clock_time_ms keeps the same value in the current collection
@@ -92,6 +103,8 @@
optional Battery battery = 1;
message BatteryDischarge {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
// Discharged battery percentage points since the stats were last reset
// after charging (lower bound approximation).
optional int32 lower_bound_since_charge = 1;
@@ -142,6 +155,8 @@
// the entire duration. Field for which the conditions were not consistent
// for the entire duration should be marked MIXED.
message BatteryLevelStep {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
// How long the battery was at the current level.
optional int64 duration_ms = 1;
// Battery level
@@ -192,6 +207,8 @@
repeated int64 cpu_frequency = 7;
message DataConnection {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
enum Name {
NONE = 0;
GPRS = 1;
@@ -221,6 +238,8 @@
optional ControllerActivityProto global_wifi_controller = 11;
message GlobalNetwork {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
// Total Bytes received on mobile connections.
optional int64 mobile_bytes_rx = 1;
// Total Bytes transmitted on mobile connections.
@@ -245,6 +264,8 @@
optional GlobalNetwork global_network = 12;
message GlobalWifi {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
// The amount of time that wifi has been on while the device was running on
// battery.
optional int64 on_duration_ms = 1;
@@ -257,6 +278,8 @@
// Kernel wakelock metrics are only recorded when the device is unplugged
// *and* the screen is off.
message KernelWakelock {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
optional string name = 1;
// Kernel wakelock stats aren't apportioned across all kernel wakelocks (as
// app wakelocks stats are).
@@ -267,6 +290,8 @@
repeated KernelWakelock kernel_wakelock = 14;
message Misc {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
optional int64 screen_on_duration_ms = 1;
optional int64 phone_on_duration_ms = 2;
optional int64 full_wakelock_total_duration_ms = 3;
@@ -312,12 +337,16 @@
optional Misc misc = 15;
message PhoneSignalStrength {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
optional android.telephony.SignalStrengthProto.StrengthName name = 1;
optional TimerProto total = 2;
};
repeated PhoneSignalStrength phone_signal_strength = 16;
message PowerUseItem {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
enum Sipper {
UNKNOWN_SIPPER = 0;
IDLE = 1;
@@ -352,6 +381,8 @@
repeated PowerUseItem power_use_item = 17;
message PowerUseSummary {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
optional double battery_capacity_mah = 1;
optional double computed_power_mah = 2;
// Lower bound of actual power drained.
@@ -362,6 +393,8 @@
optional PowerUseSummary power_use_summary = 18;
message ResourcePowerManager {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
// Either StateName or StateName.VoterName.
optional string name = 1;
optional TimerProto total = 2;
@@ -370,6 +403,8 @@
repeated ResourcePowerManager resource_power_manager = 19;
message ScreenBrightness {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
enum Name {
DARK = 0; // Not screen-off.
DIM = 1;
@@ -386,18 +421,24 @@
optional TimerProto signal_scanning = 21;
message WakeupReason {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
optional string name = 1;
optional TimerProto total = 2;
};
repeated WakeupReason wakeup_reason = 22;
message WifiMulticastWakelockTotal {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
optional int64 duration_ms = 1;
optional int32 count = 2;
}
optional WifiMulticastWakelockTotal wifi_multicast_wakelock_total = 23;
message WifiSignalStrength {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
enum Name {
NONE = 0;
POOR = 1;
@@ -411,6 +452,8 @@
repeated WifiSignalStrength wifi_signal_strength = 24;
message WifiState {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
enum Name {
OFF = 0;
OFF_SCANNING = 1;
@@ -427,6 +470,8 @@
repeated WifiState wifi_state = 25;
message WifiSupplicantState {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
enum Name {
INVALID = 0;
DISCONNECTED = 1;
@@ -449,6 +494,8 @@
}
message TimerProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
// This may be an apportioned time.
optional int64 duration_ms = 1;
optional int64 count = 2;
@@ -468,14 +515,20 @@
}
message UidProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
// Combination of app ID and user ID.
optional int32 uid = 1;
// The statistics associated with a particular package.
message Package {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
optional string name = 1;
message Service {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
optional string name = 1;
// Time spent started.
optional int64 start_duration_ms = 2;
@@ -492,6 +545,8 @@
// Bluetooth misc data.
message BluetoothMisc {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
// Duration spent BLE scanning blamed on this App (i.e. apportioned to this
// app amongst all apps doing BLE scanning; see explanation of 'apportioned'
// in App's comment).
@@ -515,6 +570,8 @@
optional BluetoothMisc bluetooth_misc = 6;
message Cpu {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
// Total CPU time with processes executing in userspace. Summed up across
// multiple cores.
optional int64 user_duration_ms = 1;
@@ -529,6 +586,8 @@
// system_duration_millis, which are just approximations. Data is not
// tracked when device is charging.
message ByFrequency {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
// Index of the frequency in system.cpu_frequency. It starts from 1, to
// make it easier to analyze.
optional int32 frequency_index = 1;
@@ -551,6 +610,8 @@
}
// CPU times at different process states.
message ByProcessState {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
optional ProcessState process_state = 1;
repeated ByFrequency by_frequency = 2;
}
@@ -574,7 +635,11 @@
optional TimerProto video = 14;
message Job {
- optional string name = 1;
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional string name = 1 [
+ (android.privacy).dest = DEST_EXPLICIT
+ ];
// Job times aren't apportioned.
optional TimerProto total = 2;
optional TimerProto background = 3;
@@ -582,10 +647,16 @@
repeated Job jobs = 15;
message JobCompletion {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
// Job name.
- optional string name = 1;
+ optional string name = 1 [
+ (android.privacy).dest = DEST_EXPLICIT
+ ];
message ReasonCount {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
optional android.app.JobParametersProto.CancelReason name = 1;
optional int32 count = 2;
}
@@ -594,6 +665,8 @@
repeated JobCompletion job_completion = 16;
message Network {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
// Mobile data traffic (total, background + foreground).
optional int64 mobile_bytes_rx = 1;
optional int64 mobile_bytes_tx = 2;
@@ -631,6 +704,8 @@
// TODO: combine System and App messages?
message PowerUseItem {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
// Estimated power use in mAh.
optional double computed_power_mah = 1;
// Starting in Oreo, Battery Settings has two modes to display the battery
@@ -648,6 +723,8 @@
// Durations are not pooled/apportioned.
message Process {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
optional string name = 1;
// Time spent executing in user code.
optional int64 user_duration_ms = 2;
@@ -665,6 +742,8 @@
repeated Process process = 19;
message StateTime {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
// All of these (non-deprecated) states are mutually exclusive and can be
// added together to find the total time a uid has had any processes running
// at all.
@@ -706,6 +785,8 @@
repeated StateTime states = 20;
message Sensor {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
optional int32 id = 1;
optional TimerProto apportioned = 2;
// Background times aren't apportioned.
@@ -714,7 +795,11 @@
repeated Sensor sensors = 21;
message Sync {
- optional string name = 1;
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional string name = 1 [
+ (android.privacy).dest = DEST_EXPLICIT
+ ];
// Sync times aren't apportioned.
optional TimerProto total = 2;
optional TimerProto background = 3;
@@ -722,6 +807,8 @@
repeated Sync syncs = 22;
message UserActivity {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
optional android.os.PowerManagerProto.UserActivityEvent name = 1;
optional int32 count = 2;
};
@@ -736,6 +823,8 @@
// wakelocks. AggregatedWakelock, on the other hand, holds overall per-app
// wakelock data.
message AggregatedWakelock {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
// The total duration that the app spent holding partial wakelocks.
// It includes both foreground + background use.
optional int64 partial_duration_ms = 1;
@@ -747,7 +836,11 @@
optional AggregatedWakelock aggregated_wakelock = 24;
message Wakelock {
- optional string name = 1;
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional string name = 1 [
+ (android.privacy).dest = DEST_EXPLICIT
+ ];
// Full wakelocks keep the screen on. Based on
// PowerManager.SCREEN_BRIGHT_WAKE_LOCK (deprecated in API 13) and
@@ -776,14 +869,20 @@
repeated Wakelock wakelocks = 25;
message WakeupAlarm {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
// Wakeup alarm name.
- optional string name = 1;
+ optional string name = 1 [
+ (android.privacy).dest = DEST_EXPLICIT
+ ];
// Only includes counts when screen-off (& on battery).
optional int32 count = 2;
}
repeated WakeupAlarm wakeup_alarm = 26;
message Wifi {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
// Duration holding Wifi-lock. This time is apportioned.
optional int64 full_wifi_lock_duration_ms = 1;
// Duration running Wifi. This time is apportioned.
diff --git a/core/proto/android/os/batterytype.proto b/core/proto/android/os/batterytype.proto
index 75d0dd3..2388c1e 100644
--- a/core/proto/android/os/batterytype.proto
+++ b/core/proto/android/os/batterytype.proto
@@ -20,6 +20,10 @@
option java_multiple_files = true;
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
+
message BatteryTypeProto {
- optional string type = 1;
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional string type = 1;
}
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index e5efb90..828a55f 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -226,7 +226,10 @@
(section).args = "activity --proto service"
];
- optional com.android.server.am.proto.ProcessProto amprocesses = 3015;
+ optional com.android.server.am.proto.ProcessesProto amprocesses = 3015 [
+ (section).type = SECTION_DUMPSYS,
+ (section).args = "activity --proto processes"
+ ];
optional com.android.server.AlarmManagerServiceProto alarm = 3016 [
(section).type = SECTION_DUMPSYS,
diff --git a/core/proto/android/os/powermanager.proto b/core/proto/android/os/powermanager.proto
index e9f409d..8e0a607 100644
--- a/core/proto/android/os/powermanager.proto
+++ b/core/proto/android/os/powermanager.proto
@@ -19,6 +19,8 @@
option java_multiple_files = true;
+import "frameworks/base/core/proto/android/os/worksource.proto";
+
message PowerManagerProto {
/* User activity events in PowerManager.java. */
enum UserActivityEvent {
@@ -85,6 +87,14 @@
// the dozing state.
DRAW_WAKE_LOCK = 128;
}
+
+ // WakeLock class in android.os.PowerManager, it is the one used by sdk
+ message WakeLockProto {
+ optional string hex_string = 1;
+ optional bool held = 2;
+ optional int32 internal_count = 3;
+ optional WorkSourceProto work_source = 4;
+ }
}
message PowerManagerInternalProto {
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index d3ca496..1434d82 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -18,11 +18,17 @@
package com.android.server.am.proto;
+import "frameworks/base/core/proto/android/app/activitymanager.proto";
import "frameworks/base/core/proto/android/app/notification.proto";
+import "frameworks/base/core/proto/android/app/profilerinfo.proto";
+import "frameworks/base/core/proto/android/content/component_name.proto";
+import "frameworks/base/core/proto/android/content/configuration.proto";
import "frameworks/base/core/proto/android/content/intent.proto";
+import "frameworks/base/core/proto/android/content/package_item_info.proto";
import "frameworks/base/core/proto/android/graphics/rect.proto";
import "frameworks/base/core/proto/android/internal/processstats.proto";
import "frameworks/base/core/proto/android/os/looper.proto";
+import "frameworks/base/core/proto/android/os/powermanager.proto";
import "frameworks/base/core/proto/android/server/intentresolver.proto";
import "frameworks/base/core/proto/android/server/windowmanagerservice.proto";
import "frameworks/base/core/proto/android/util/common.proto";
@@ -36,7 +42,7 @@
optional ActiveServicesProto services = 3;
- optional ProcessProto processes = 4;
+ optional ProcessesProto processes = 4;
}
// "dumpsys activity --proto activities"
@@ -130,6 +136,7 @@
optional int32 user_id = 4;
optional int32 app_id = 5;
optional int32 isolated_app_id = 6;
+ optional bool persistent = 7;
}
message BroadcastRecordProto {
@@ -459,7 +466,7 @@
WAIVE_PRIORITY = 6;
IMPORTANT = 7;
ADJUST_WITH_ACTIVITY = 8;
- FG_SERVICE_WHILE_WAKE = 9;
+ FG_SERVICE_WHILE_AWAKE = 9;
FG_SERVICE = 10;
TREAT_LIKE_ACTIVITY = 11;
VISIBLE = 12;
@@ -492,5 +499,362 @@
}
// TODO: "dumpsys activity --proto processes"
-message ProcessProto {
+message ProcessesProto {
+ repeated ProcessRecordProto procs = 1;
+ repeated ProcessRecordProto isolated_procs = 2;
+ repeated ActiveInstrumentationProto active_instrumentations = 3;
+ repeated UidRecordProto active_uids = 4;
+ repeated UidRecordProto validate_uids = 5;
+
+ // Process LRU list (sorted by oom_adj)
+ message LruProcesses {
+ optional int32 size = 1;
+ optional int32 non_act_at = 2;
+ optional int32 non_svc_at = 3;
+ repeated ProcessOomProto list = 4;
+ }
+ optional LruProcesses lru_procs = 6;
+ repeated ProcessRecordProto pids_self_locked = 7;
+ // Foreground Processes
+ repeated ImportanceTokenProto important_procs = 8;
+ // Persisent processes that are starting
+ repeated ProcessRecordProto persistent_starting_procs = 9;
+ // Processes that are being removed
+ repeated ProcessRecordProto removed_procs = 10;
+ // Processes that are on old until the system is ready
+ repeated ProcessRecordProto on_hold_procs = 11;
+ // Processes that are waiting to GC
+ repeated ProcessToGcProto gc_procs = 12;
+ optional AppErrorsProto app_errors = 13;
+ optional UserControllerProto user_controller = 14;
+ optional ProcessRecordProto home_proc = 15;
+ optional ProcessRecordProto previous_proc = 16;
+ optional int64 previous_proc_visible_time_ms = 17;
+ optional ProcessRecordProto heavy_weight_proc = 18;
+ optional .android.content.ConfigurationProto global_configuration = 19;
+ // ActivityStackSupervisorProto dumps these values as well, still here?
+ // repeated ActivityDisplayProto displays = 20;
+
+ optional bool config_will_change = 21;
+
+ message ScreenCompatPackage {
+ optional string package = 1;
+ optional int32 mode = 2;
+ }
+ repeated ScreenCompatPackage screen_compat_packages = 22;
+
+ message UidObserverRegistrationProto {
+ optional int32 uid = 1;
+ optional string package = 2;
+ repeated .android.app.UidObserverFlag flags = 3;
+ optional int32 cut_point = 4; // only available when UID_OBSERVER_PROCSTATE is on
+
+ message ProcState {
+ optional int32 uid = 1;
+ optional int32 state = 2;
+ }
+ repeated ProcState last_proc_states = 5;
+ }
+ repeated UidObserverRegistrationProto uid_observers = 23;
+ repeated int32 device_idle_whitelist = 24;
+ repeated int32 device_idle_temp_whitelist = 25;
+
+ message PendingTempWhitelist {
+ optional int32 target_uid = 1;
+ optional int64 duration_ms = 2;
+ optional string tag = 3;
+ }
+ repeated PendingTempWhitelist pending_temp_whitelist = 26;
+
+ message SleepStatus {
+ optional .android.os.PowerManagerInternalProto.Wakefulness wakefulness = 1;
+ repeated string sleep_tokens = 2;
+ optional bool sleeping = 3;
+ optional bool shutting_down = 4;
+ optional bool test_pss_mode = 5;
+ }
+ optional SleepStatus sleep_status = 27;
+
+ message VoiceProto {
+ optional string session = 1;
+ optional .android.os.PowerManagerProto.WakeLockProto wakelock = 2;
+ }
+ optional VoiceProto running_voice = 28;
+
+ message VrControllerProto {
+ enum VrMode {
+ FLAG_NON_VR_MODE = 0;
+ FLAG_VR_MODE = 1;
+ FLAG_PERSISTENT_VR_MODE = 2;
+ }
+ repeated VrMode vr_mode = 1;
+ optional int32 render_thread_id = 2;
+ }
+ optional VrControllerProto vr_controller = 29;
+
+ message DebugApp {
+ optional string debug_app = 1;
+ optional string orig_debug_app = 2;
+ optional bool debug_transient = 3;
+ optional bool orig_wait_for_debugger = 4;
+ }
+ optional DebugApp debug = 30;
+ optional AppTimeTrackerProto current_tracker = 31;
+
+ message MemWatchProcess {
+ message Process {
+ optional string name = 1;
+
+ message MemStats {
+ optional int32 uid = 1;
+ optional string size = 2;
+ optional string report_to = 3;
+ }
+ repeated MemStats mem_stats = 2;
+ }
+ repeated Process procs = 1;
+
+ message Dump {
+ optional string proc_name = 1;
+ optional string file = 2;
+ optional int32 pid = 3;
+ optional int32 uid = 4;
+ }
+ optional Dump dump = 2;
+ }
+ optional MemWatchProcess mem_watch_processes = 32;
+ optional string track_allocation_app = 33;
+
+ message Profile {
+ optional string app_name = 1;
+ optional ProcessRecordProto proc = 2;
+ optional .android.app.ProfilerInfoProto info = 3;
+ optional int32 type = 4;
+ }
+ optional Profile profile = 34;
+ optional string native_debugging_app = 35;
+ optional bool always_finish_activities = 36;
+
+ message Controller {
+ optional string controller = 1;
+ optional bool is_a_monkey = 2;
+ }
+ optional Controller controller = 37;
+
+ optional int32 total_persistent_procs = 38;
+ optional bool processes_ready = 39;
+ optional bool system_ready = 40;
+ optional bool booted = 41;
+ optional int32 factory_test = 42;
+ optional bool booting = 43;
+ optional bool call_finish_booting = 44;
+ optional bool boot_animation_complete = 45;
+ optional int64 last_power_check_uptime_ms = 46;
+ optional .android.os.PowerManagerProto.WakeLockProto going_to_sleep = 47;
+ optional .android.os.PowerManagerProto.WakeLockProto launching_activity = 48;
+ optional int32 adj_seq = 49;
+ optional int32 lru_seq = 50;
+ optional int32 num_non_cached_procs = 51;
+ optional int32 num_cached_hidden_procs = 52;
+ optional int32 num_service_procs = 53;
+ optional int32 new_num_service_procs = 54;
+ optional bool allow_lower_mem_level = 55;
+ optional int32 last_memory_level = 56;
+ optional int32 last_num_processes = 57;
+ optional .android.util.Duration last_idle_time = 58;
+ optional int64 low_ram_since_last_idle_ms = 59;
+}
+
+message ActiveInstrumentationProto {
+ optional .android.content.ComponentNameProto class = 1;
+ optional bool finished = 2;
+ repeated ProcessRecordProto running_processes = 3;
+ repeated string target_processes = 4;
+ optional .android.content.pm.ApplicationInfoProto target_info = 5;
+ optional string profile_file = 6;
+ optional string watcher = 7;
+ optional string ui_automation_connection = 8;
+ optional string arguments = 9;
+}
+
+// Proto definition of com.android.server.am.UidRecord.java
+message UidRecordProto {
+ optional string hex_hash = 1;
+ optional int32 uid = 2;
+ optional .android.app.ProcessState current = 3;
+ optional bool ephemeral = 4;
+ optional bool fg_services = 5;
+ optional bool whilelist = 6;
+ optional .android.util.Duration last_background_time = 7;
+ optional bool idle = 8;
+
+ enum Change {
+ CHANGE_GONE = 0;
+ CHANGE_IDLE = 1;
+ CHANGE_ACTIVE = 2;
+ CHANGE_CACHED = 3;
+ CHANGE_UNCACHED = 4;
+ }
+ repeated Change last_reported_changes = 9;
+ optional int32 num_procs = 10;
+
+ message ProcStateSequence {
+ optional int64 cururent = 1;
+ optional int64 last_network_updated = 2;
+ optional int64 last_dispatched = 3;
+ }
+ optional ProcStateSequence network_state_update = 11;
+}
+
+// proto of class ImportanceToken in ActivityManagerService
+message ImportanceTokenProto {
+ optional int32 pid = 1;
+ optional string token = 2;
+ optional string reason = 3;
+}
+
+message ProcessOomProto {
+ optional bool persistent = 1;
+ optional int32 num = 2;
+ optional string oom_adj = 3;
+
+ // Activity manager's version of Process enum, see ProcessList.java
+ enum SchedGroup {
+ SCHED_GROUP_UNKNOWN = -1;
+ SCHED_GROUP_BACKGROUND = 0;
+ SCHED_GROUP_DEFAULT = 1;
+ SCHED_GROUP_TOP_APP = 2;
+ SCHED_GROUP_TOP_APP_BOUND = 3;
+ }
+ optional SchedGroup sched_group = 4 [ default = SCHED_GROUP_UNKNOWN];
+
+ oneof Foreground {
+ bool activities = 5;
+ bool services = 6;
+ }
+
+ optional .android.app.ProcessState state = 7;
+ optional int32 trim_memory_level = 8;
+ optional ProcessRecordProto proc = 9;
+ optional string adj_type = 10;
+
+ oneof AdjTarget {
+ .android.content.ComponentNameProto adj_target_component_name = 11;
+ string adj_target_object = 12;
+ }
+
+ oneof AdjSource {
+ ProcessRecordProto adj_source_proc = 13;
+ string adj_source_object = 14;
+ }
+
+ message Detail {
+ optional int32 max_adj = 1;
+ optional int32 cur_raw_adj = 2;
+ optional int32 set_raw_adj = 3;
+ optional int32 cur_adj = 4;
+ optional int32 set_adj = 5;
+ optional .android.app.ProcessState current_state = 7;
+ optional .android.app.ProcessState set_state = 8;
+ optional string last_pss = 9;
+ optional string last_swap_pss = 10;
+ optional string last_cached_pss = 11;
+ optional bool cached = 12;
+ optional bool empty = 13;
+ optional bool has_above_client = 14;
+
+ // only make sense if process is a service
+ message CpuRunTime {
+ optional int64 over_ms = 1;
+ optional int64 used_ms = 2;
+ optional float ultilization = 3; // ratio of cpu time usage
+ }
+ optional CpuRunTime service_run_time = 15;
+ }
+ optional Detail detail = 15;
+}
+
+message ProcessToGcProto {
+ optional ProcessRecordProto proc = 1;
+ optional bool report_low_memory = 2;
+ optional int64 now_uptime_ms = 3;
+ optional int64 last_gced_ms = 4;
+ optional int64 last_low_memory_ms = 5;
+}
+
+// sync with com.android.server.am.AppErrors.java
+message AppErrorsProto {
+
+ optional int64 now_uptime_ms = 1;
+
+ message ProcessCrashTime {
+ optional string process_name = 1;
+
+ message Entry {
+ optional int32 uid = 1;
+ optional int64 last_crashed_at_ms = 2;
+ }
+ repeated Entry entries = 2;
+ }
+ repeated ProcessCrashTime process_crash_times = 2;
+
+ message BadProcess {
+ optional string process_name = 1;
+
+ message Entry {
+ optional int32 uid = 1;
+ optional int64 crashed_at_ms = 2;
+ optional string short_msg = 3;
+ optional string long_msg = 4;
+ optional string stack = 5;
+ }
+ repeated Entry entries = 2;
+ }
+ repeated BadProcess bad_processes = 3;
+}
+
+// sync with com.android.server.am.UserState.java
+message UserStateProto {
+ enum State {
+ STATE_BOOTING = 0;
+ STATE_RUNNING_LOCKED = 1;
+ STATE_RUNNING_UNLOCKING = 2;
+ STATE_RUNNING_UNLOCKED = 3;
+ STATE_STOPPING = 4;
+ STATE_SHUTDOWN = 5;
+ }
+ optional State state = 1;
+ optional bool switching = 2;
+}
+
+// sync with com.android.server.am.UserController.java
+message UserControllerProto {
+ message User {
+ optional int32 id = 1;
+ optional UserStateProto state = 2;
+ }
+ repeated User started_users = 1;
+ repeated int32 started_user_array = 2;
+ repeated int32 user_lru = 3;
+
+ message UserProfile {
+ optional int32 user = 1;
+ optional int32 profile = 2;
+ }
+ repeated UserProfile user_profile_group_ids = 4;
+}
+
+// sync with com.android.server.am.AppTimeTracker.java
+message AppTimeTrackerProto {
+ optional string receiver = 1;
+ optional int64 total_duration_ms = 2;
+
+ message PackageTime {
+ optional string package = 1;
+ optional int64 duration_ms = 2;
+ }
+ repeated PackageTime package_times = 3;
+
+ optional .android.util.Duration started_time = 4;
+ optional string started_package = 5;
}
diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto
index f72ca62..739fca3 100644
--- a/core/proto/android/server/jobscheduler.proto
+++ b/core/proto/android/server/jobscheduler.proto
@@ -558,4 +558,6 @@
optional int64 last_successful_run_time = 22;
optional int64 last_failed_run_time = 23;
+
+ optional int64 internal_flags = 24;
}
diff --git a/core/proto/android/service/battery.proto b/core/proto/android/service/battery.proto
index 4cb7fd3..42fa72c 100644
--- a/core/proto/android/service/battery.proto
+++ b/core/proto/android/service/battery.proto
@@ -21,8 +21,11 @@
option java_outer_classname = "BatteryServiceProto";
import "frameworks/base/core/proto/android/os/batterymanager.proto";
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
message BatteryServiceDumpProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
enum BatteryStatus {
BATTERY_STATUS_INVALID = 0;
BATTERY_STATUS_UNKNOWN = 1;
diff --git a/core/proto/android/service/batterystats.proto b/core/proto/android/service/batterystats.proto
index 54d3f40..e31e7f3 100644
--- a/core/proto/android/service/batterystats.proto
+++ b/core/proto/android/service/batterystats.proto
@@ -21,7 +21,10 @@
option java_outer_classname = "BatteryStatsServiceProto";
import "frameworks/base/core/proto/android/os/batterystats.proto";
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
message BatteryStatsServiceDumpProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
optional android.os.BatteryStatsProto batterystats = 1;
}
diff --git a/core/proto/android/service/notification.proto b/core/proto/android/service/notification.proto
index 7a0e152..65df89a 100644
--- a/core/proto/android/service/notification.proto
+++ b/core/proto/android/service/notification.proto
@@ -24,6 +24,7 @@
import "frameworks/base/core/proto/android/app/notification_channel_group.proto";
import "frameworks/base/core/proto/android/app/notificationmanager.proto";
import "frameworks/base/core/proto/android/content/component_name.proto";
+import "frameworks/base/core/proto/android/media/audioattributes.proto";
message NotificationServiceDumpProto {
repeated NotificationRecordProto records = 1;
@@ -55,7 +56,7 @@
optional int32 flags = 3;
optional string channelId = 4;
optional string sound = 5;
- optional int32 sound_usage = 6;
+ optional .android.media.AudioAttributesProto audio_attributes = 6;
optional bool can_vibrate = 7;
optional bool can_show_light = 8;
optional string group_key = 9;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 93d852c..081c92c 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -327,6 +327,10 @@
<protected-broadcast android:name="com.android.server.wifi.ConnectToNetworkNotification.CONNECT_TO_NETWORK" />
<protected-broadcast android:name="com.android.server.wifi.ConnectToNetworkNotification.PICK_WIFI_NETWORK" />
<protected-broadcast android:name="com.android.server.wifi.ConnectToNetworkNotification.PICK_NETWORK_AFTER_FAILURE" />
+ <protected-broadcast android:name="com.android.server.wifi.wakeup.DISMISS_NOTIFICATION" />
+ <protected-broadcast android:name="com.android.server.wifi.wakeup.OPEN_WIFI_PREFERENCES" />
+ <protected-broadcast android:name="com.android.server.wifi.wakeup.OPEN_WIFI_SETTINGS" />
+ <protected-broadcast android:name="com.android.server.wifi.wakeup.TURN_OFF_WIFI_WAKE" />
<protected-broadcast android:name="android.net.wifi.WIFI_STATE_CHANGED" />
<protected-broadcast android:name="android.net.wifi.WIFI_AP_STATE_CHANGED" />
<protected-broadcast android:name="android.net.wifi.WIFI_CREDENTIAL_CHANGED" />
diff --git a/core/res/res/layout/notification_template_material_ambient.xml b/core/res/res/layout/notification_template_material_ambient.xml
index 435289d..19c4d23 100644
--- a/core/res/res/layout/notification_template_material_ambient.xml
+++ b/core/res/res/layout/notification_template_material_ambient.xml
@@ -57,7 +57,7 @@
android:singleLine="true"
android:ellipsize="marquee"
android:fadingEdge="horizontal"
- android:textSize="20sp"
+ android:textSize="24sp"
android:textColor="#ffffffff"
/>
<TextView android:id="@+id/text"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 4d80b2f..354d658 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1897,6 +1897,7 @@
<enum name="KEYCODE_SYSTEM_NAVIGATION_LEFT" value="282" />
<enum name="KEYCODE_SYSTEM_NAVIGATION_RIGHT" value="283" />
<enum name="KEYCODE_ALL_APPS" value="284" />
+ <enum name="KEYCODE_REFRESH" value="285" />
</attr>
<!-- ***************************************************************** -->
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 9cab9fa..88549b5 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1321,6 +1321,9 @@
<string-array name="fingerprint_acquired_vendor">
</string-array>
+ <!-- Message shown by the fingerprint dialog when fingerprint is not recognized -->
+ <string name="fingerprint_not_recognized">Not recognized</string>
+
<!-- Error message shown when the fingerprint hardware can't be accessed -->
<string name="fingerprint_error_hw_not_available">Fingerprint hardware not available.</string>
<!-- Error message shown when the fingerprint hardware has run out of room for storing fingerprints -->
@@ -1329,6 +1332,8 @@
<string name="fingerprint_error_timeout">Fingerprint time out reached. Try again.</string>
<!-- Generic error message shown when the fingerprint operation (e.g. enrollment or authentication) is canceled. Generally not shown to the user-->
<string name="fingerprint_error_canceled">Fingerprint operation canceled.</string>
+ <!-- Generic error message shown when the fingerprint authentication operation is canceled due to user input. Generally not shown to the user -->
+ <string name="fingerprint_error_user_canceled">Fingerprint operation canceled by user.</string>
<!-- Generic error message shown when the fingerprint operation fails because too many attempts have been made. -->
<string name="fingerprint_error_lockout">Too many attempts. Try again later.</string>
<!-- Generic error message shown when the fingerprint operation fails because strong authentication is required -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 4ef0a6c..1711ec9 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2341,9 +2341,11 @@
<java-symbol type="string" name="fingerprint_acquired_too_fast" />
<java-symbol type="array" name="fingerprint_acquired_vendor" />
<java-symbol type="string" name="fingerprint_error_canceled" />
+ <java-symbol type="string" name="fingerprint_error_user_canceled" />
<java-symbol type="string" name="fingerprint_error_lockout" />
<java-symbol type="string" name="fingerprint_error_lockout_permanent" />
<java-symbol type="string" name="fingerprint_name_template" />
+ <java-symbol type="string" name="fingerprint_not_recognized" />
<!-- Fingerprint config -->
<java-symbol type="integer" name="config_fingerprintMaxTemplatesPerUser"/>
diff --git a/core/tests/benchmarks/src/com/android/internal/net/NetworkStatsFactoryBenchmark.java b/core/tests/benchmarks/src/com/android/internal/net/NetworkStatsFactoryBenchmark.java
index e62fbd6..c213464 100644
--- a/core/tests/benchmarks/src/com/android/internal/net/NetworkStatsFactoryBenchmark.java
+++ b/core/tests/benchmarks/src/com/android/internal/net/NetworkStatsFactoryBenchmark.java
@@ -53,7 +53,7 @@
stats, mStats.getAbsolutePath(), NetworkStats.UID_ALL,
// Looks like this was broken by change d0c5b9abed60b7bc056d026bf0f2b2235410fb70
// Fixed compilation problem but needs addressing properly.
- new String[0], 999);
+ new String[0], 999, false);
}
}
}
diff --git a/data/keyboards/Generic.kl b/data/keyboards/Generic.kl
index 74f8c71..8699cb4 100644
--- a/data/keyboards/Generic.kl
+++ b/data/keyboards/Generic.kl
@@ -192,7 +192,7 @@
# key 170 "KEY_ISO"
key 171 MUSIC
key 172 HOME
-# key 173 "KEY_REFRESH"
+key 173 REFRESH
# key 174 "KEY_EXIT"
# key 175 "KEY_MOVE"
# key 176 "KEY_EDIT"
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index d7861e3..44a2ff9 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -880,7 +880,9 @@
}
/** @hide */
- public void toProto(ProtoOutputStream proto) {
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+
proto.write(AudioAttributesProto.USAGE, mUsage);
proto.write(AudioAttributesProto.CONTENT_TYPE, mContentType);
proto.write(AudioAttributesProto.FLAGS, mFlags);
@@ -892,6 +894,8 @@
}
}
// TODO: is the data in mBundle useful for debugging?
+
+ proto.end(token);
}
/** @hide */
diff --git a/media/java/android/media/IMediaSession2.aidl b/media/java/android/media/IMediaSession2.aidl
index bc8121e..078b611 100644
--- a/media/java/android/media/IMediaSession2.aidl
+++ b/media/java/android/media/IMediaSession2.aidl
@@ -47,6 +47,11 @@
PlaybackState getPlaybackState();
//////////////////////////////////////////////////////////////////////////////////////////////
+ // Get library service specific
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ oneway void getBrowserRoot(IMediaSession2Callback callback, in Bundle rootHints);
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
// Callbacks -- remove them
//////////////////////////////////////////////////////////////////////////////////////////////
/**
diff --git a/media/java/android/media/IMediaSession2Callback.aidl b/media/java/android/media/IMediaSession2Callback.aidl
index 0058e79..a63b6bf 100644
--- a/media/java/android/media/IMediaSession2Callback.aidl
+++ b/media/java/android/media/IMediaSession2Callback.aidl
@@ -44,4 +44,9 @@
// Follow-up TODO: Add similar functions to the session.
// TODO(jaewan): Is term 'accepted/rejected' correct? For permission, 'grant' is used.
void onConnectionChanged(IMediaSession2 sessionBinder, in Bundle commandGroup);
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ // Browser sepcific
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ void onGetRootResult(in Bundle rootHints, String rootMediaId, in Bundle rootExtra);
}
diff --git a/media/java/android/media/MediaBrowser2.java b/media/java/android/media/MediaBrowser2.java
new file mode 100644
index 0000000..fa00902
--- /dev/null
+++ b/media/java/android/media/MediaBrowser2.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 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.media;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.media.update.ApiLoader;
+import android.media.update.MediaBrowser2Provider;
+import android.os.Bundle;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Browses media content offered by a {@link MediaLibraryService2}.
+ * @hide
+ */
+public class MediaBrowser2 extends MediaController2 {
+ // Equals to the ((MediaBrowser2Provider) getProvider())
+ private final MediaBrowser2Provider mProvider;
+
+ /**
+ * Callback to listen events from {@link MediaLibraryService2}.
+ */
+ public abstract static class BrowserCallback extends MediaController2.ControllerCallback {
+ /**
+ * Called with the result of {@link #getBrowserRoot(Bundle)}.
+ * <p>
+ * {@code rootMediaId} and {@code rootExtra} can be {@code null} if the browser root isn't
+ * available.
+ *
+ * @param rootHints rootHints that you previously requested.
+ * @param rootMediaId media id of the browser root. Can be {@code null}
+ * @param rootExtra extra of the browser root. Can be {@code null}
+ */
+ public abstract void onGetRootResult(Bundle rootHints, @Nullable String rootMediaId,
+ @Nullable Bundle rootExtra);
+ }
+
+ public MediaBrowser2(Context context, SessionToken token, BrowserCallback callback,
+ Executor executor) {
+ super(context, token, callback, executor);
+ mProvider = (MediaBrowser2Provider) getProvider();
+ }
+
+ @Override
+ MediaBrowser2Provider createProvider(Context context, SessionToken token,
+ ControllerCallback callback, Executor executor) {
+ return ApiLoader.getProvider(context)
+ .createMediaBrowser2(this, context, token, (BrowserCallback) callback, executor);
+ }
+
+ public void getBrowserRoot(Bundle rootHints) {
+ mProvider.getBrowserRoot_impl(rootHints);
+ }
+}
diff --git a/media/java/android/media/MediaController2.java b/media/java/android/media/MediaController2.java
index 550eee2..9112450 100644
--- a/media/java/android/media/MediaController2.java
+++ b/media/java/android/media/MediaController2.java
@@ -104,7 +104,13 @@
// session whose session binder is only valid while it's active.
// prevent a controller from reusable after the
// session is released and recreated.
- mProvider = ApiLoader.getProvider(context)
+ mProvider = createProvider(context, token, callback, executor);
+ }
+
+ MediaController2Provider createProvider(@NonNull Context context,
+ @NonNull SessionToken token, @NonNull ControllerCallback callback,
+ @NonNull Executor executor) {
+ return ApiLoader.getProvider(context)
.createMediaController2(this, context, token, callback, executor);
}
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index 690d740..b908c21 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -26,6 +26,7 @@
import android.os.Looper;
import android.os.Message;
import android.os.Parcel;
+import android.os.PersistableBundle;
import android.util.Log;
import dalvik.system.CloseGuard;
import java.lang.annotation.Retention;
@@ -1201,7 +1202,6 @@
public native void setPropertyByteArray(@NonNull @ArrayProperty
String propertyName, @NonNull byte[] value);
-
private static final native void setCipherAlgorithmNative(
@NonNull MediaDrm drm, @NonNull byte[] sessionId, @NonNull String algorithm);
@@ -1228,6 +1228,25 @@
@NonNull byte[] keyId, @NonNull byte[] message, @NonNull byte[] signature);
/**
+ * Return Metrics data about the current MediaDrm instance.
+ *
+ * @return a {@link PersistableBundle} containing the set of attributes and values
+ * available for this instance of MediaDrm.
+ * The attributes are described in {@link MetricsConstants}.
+ *
+ * Additional vendor-specific fields may also be present in
+ * the return value.
+ *
+ * @hide - not part of the public API at this time
+ */
+ public PersistableBundle getMetrics() {
+ PersistableBundle bundle = getMetricsNative();
+ return bundle;
+ }
+
+ private native PersistableBundle getMetricsNative();
+
+ /**
* In addition to supporting decryption of DASH Common Encrypted Media, the
* MediaDrm APIs provide the ability to securely deliver session keys from
* an operator's session key server to a client device, based on the factory-installed
@@ -1531,4 +1550,31 @@
System.loadLibrary("media_jni");
native_init();
}
+
+ /**
+ * Definitions for the metrics that are reported via the
+ * {@link #getMetrics} call.
+ *
+ * @hide - not part of the public API at this time
+ */
+ public final static class MetricsConstants
+ {
+ private MetricsConstants() {}
+
+ /**
+ * Key to extract the number of successful {@link #openSession} calls
+ * from the {@link PersistableBundle} returned by a
+ * {@link #getMetrics} call.
+ */
+ public static final String OPEN_SESSION_OK_COUNT
+ = "/drm/mediadrm/open_session/ok/count";
+
+ /**
+ * Key to extract the number of failed {@link #openSession} calls
+ * from the {@link PersistableBundle} returned by a
+ * {@link #getMetrics} call.
+ */
+ public static final String OPEN_SESSION_ERROR_COUNT
+ = "/drm/mediadrm/open_session/error/count";
+ }
}
diff --git a/media/java/android/media/MediaLibraryService2.java b/media/java/android/media/MediaLibraryService2.java
new file mode 100644
index 0000000..bbc9407
--- /dev/null
+++ b/media/java/android/media/MediaLibraryService2.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright 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.media;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.media.MediaSession2.BuilderBase;
+import android.media.MediaSession2.ControllerInfo;
+import android.media.update.ApiLoader;
+import android.media.update.MediaSessionService2Provider;
+import android.os.Bundle;
+import android.service.media.MediaBrowserService.BrowserRoot;
+
+/**
+ * Base class for media library services.
+ * <p>
+ * Media library services enable applications to browse media content provided by an application
+ * and ask the application to start playing it. They may also be used to control content that
+ * is already playing by way of a {@link MediaSession2}.
+ * <p>
+ * To extend this class, adding followings directly to your {@code AndroidManifest.xml}.
+ * <pre>
+ * <service android:name="component_name_of_your_implementation" >
+ * <intent-filter>
+ * <action android:name="android.media.MediaLibraryService2" />
+ * </intent-filter>
+ * </service></pre>
+ * <p>
+ * A {@link MediaLibraryService2} is extension of {@link MediaSessionService2}. IDs shouldn't
+ * be shared between the {@link MediaSessionService2} and {@link MediaSession2}. By
+ * default, an empty string will be used for ID of the service. If you want to specify an ID,
+ * declare metadata in the manifest as follows.
+ * @hide
+ */
+// TODO(jaewan): Unhide
+public abstract class MediaLibraryService2 extends MediaSessionService2 {
+ /**
+ * This is the interface name that a service implementing a session service should say that it
+ * support -- that is, this is the action it uses for its intent filter.
+ */
+ public static final String SERVICE_INTERFACE = "android.media.MediaLibraryService2";
+
+ /**
+ * Session for the media library service.
+ */
+ public class MediaLibrarySession extends MediaSession2 {
+ MediaLibrarySession(Context context, MediaPlayerBase player, String id,
+ SessionCallback callback) {
+ super(context, player, id, callback);
+ }
+ // TODO(jaewan): Place public methods here.
+ }
+
+ public static abstract class MediaLibrarySessionCallback extends MediaSession2.SessionCallback {
+ /**
+ * Called to get the root information for browsing by a particular client.
+ * <p>
+ * The implementation should verify that the client package has permission
+ * to access browse media information before returning the root id; it
+ * should return null if the client is not allowed to access this
+ * information.
+ *
+ * @param controllerInfo information of the controller requesting access to browse media.
+ * @param rootHints An optional bundle of service-specific arguments to send
+ * to the media browser service when connecting and retrieving the
+ * root id for browsing, or null if none. The contents of this
+ * bundle may affect the information returned when browsing.
+ * @return The {@link BrowserRoot} for accessing this app's content or null.
+ * @see BrowserRoot#EXTRA_RECENT
+ * @see BrowserRoot#EXTRA_OFFLINE
+ * @see BrowserRoot#EXTRA_SUGGESTED
+ */
+ public abstract @Nullable BrowserRoot onGetRoot(
+ @NonNull ControllerInfo controllerInfo, @Nullable Bundle rootHints);
+ }
+
+ /**
+ * Builder for {@link MediaLibrarySession}.
+ */
+ // TODO(jaewan): Move this to updatable.
+ public class MediaLibrarySessionBuilder
+ extends BuilderBase<MediaLibrarySessionBuilder, MediaLibrarySessionCallback> {
+ public MediaLibrarySessionBuilder(
+ @NonNull Context context, @NonNull MediaPlayerBase player,
+ @NonNull MediaLibrarySessionCallback callback) {
+ super(context, player);
+ setSessionCallback(callback);
+ }
+
+ @Override
+ public MediaLibrarySessionBuilder setSessionCallback(
+ @NonNull MediaLibrarySessionCallback callback) {
+ if (callback == null) {
+ throw new IllegalArgumentException("MediaLibrarySessionCallback cannot be null");
+ }
+ return super.setSessionCallback(callback);
+ }
+
+ @Override
+ public MediaLibrarySession build() throws IllegalStateException {
+ return new MediaLibrarySession(mContext, mPlayer, mId, mCallback);
+ }
+ }
+
+ @Override
+ MediaSessionService2Provider createProvider() {
+ return ApiLoader.getProvider(this).createMediaLibraryService2(this);
+ }
+
+ /**
+ * Called when another app requested to start this service.
+ * <p>
+ * Library service will accept or reject the connection with the
+ * {@link MediaLibrarySessionCallback} in the created session.
+ * <p>
+ * Service wouldn't run if {@code null} is returned or session's ID doesn't match with the
+ * expected ID that you've specified through the AndroidManifest.xml.
+ * <p>
+ * This method will be called on the main thread.
+ *
+ * @param sessionId session id written in the AndroidManifest.xml.
+ * @return a new browser session
+ * @see MediaLibrarySessionBuilder
+ * @see #getSession()
+ * @throws RuntimeException if returned session is invalid
+ */
+ @Override
+ public @NonNull abstract MediaLibrarySession onCreateSession(String sessionId);
+}
diff --git a/media/java/android/media/MediaSession2.java b/media/java/android/media/MediaSession2.java
index 807535b..33a5807 100644
--- a/media/java/android/media/MediaSession2.java
+++ b/media/java/android/media/MediaSession2.java
@@ -16,7 +16,6 @@
package android.media;
-import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
@@ -29,12 +28,9 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.Parcelable;
-import android.os.Process;
import android.text.TextUtils;
import android.util.ArraySet;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
@@ -55,20 +51,17 @@
* instead. With it, your playback can be revived even after you've finished playback. See
* {@link MediaSessionService2} for details.
* <p>
- * A session can be obtained by {@link #getInstance(Context, Handler)}. The owner of the session may
- * pass its session token to other processes to allow them to create a {@link MediaController2}
- * to interact with the session.
+ * A session can be obtained by {@link Builder}. The owner of the session may pass its session token
+ * to other processes to allow them to create a {@link MediaController2} to interact with the
+ * session.
* <p>
- * To receive transport control commands, an underlying media player must be set with
- * {@link #setPlayer(MediaPlayerBase)}. Commands will be sent to the underlying player directly
- * on the thread that had been specified by {@link #getInstance(Context, Handler)}.
+ * When a session receive transport control commands, the session sends the commands directly to
+ * the the underlying media player set by {@link Builder} or {@link #setPlayer(MediaPlayerBase)}.
* <p>
- * When an app is finished performing playback it must call
- * {@link #setPlayer(MediaPlayerBase)} with {@code null} to clean up the session and notify any
- * controllers. It's developers responsibility of cleaning the session and releasing resources.
+ * When an app is finished performing playback it must call {@link #close()} to clean up the session
+ * and notify any controllers.
* <p>
- * MediaSession2 objects should be used on the handler's thread that is initially given by
- * {@link #getInstance(Context, Handler)}.
+ * {@link MediaSession2} objects should be used on the thread on the looper.
*
* @see MediaSessionService2
* @hide
@@ -81,7 +74,7 @@
// TODO(jaewan): Should we make APIs for MediaSessionService2 public? It's helpful for
// developers that doesn't want to override from Browser, but user may not use this
// correctly.
-public final class MediaSession2 extends MediaPlayerBase {
+public class MediaSession2 extends MediaPlayerBase {
private final MediaSession2Provider mProvider;
// Note: Do not define IntDef because subclass can add more command code on top of these.
@@ -321,19 +314,16 @@
};
/**
- * Builder for {@link MediaSession2}.
- * <p>
- * Any incoming event from the {@link MediaController2} will be handled on the thread
- * that created session with the {@link Builder#build()}.
+ * Base builder class for MediaSession2 and its subclass.
+ *
+ * @hide
*/
- // TODO(jaewan): Move this to updatable
- // TODO(jaewan): Add setRatingType()
- // TODO(jaewan): Add setSessionActivity()
- public final static class Builder {
- private final Context mContext;
- private final MediaPlayerBase mPlayer;
- private String mId;
- private SessionCallback mCallback;
+ static abstract class BuilderBase
+ <T extends MediaSession2.BuilderBase<T, C>, C extends SessionCallback> {
+ final Context mContext;
+ final MediaPlayerBase mPlayer;
+ String mId;
+ C mCallback;
/**
* Constructor.
@@ -343,7 +333,8 @@
* @throws IllegalArgumentException if any parameter is null, or the player is a
* {@link MediaSession2} or {@link MediaController2}.
*/
- public Builder(@NonNull Context context, @NonNull MediaPlayerBase player) {
+ // TODO(jaewan): Also need executor
+ public BuilderBase(@NonNull Context context, @NonNull MediaPlayerBase player) {
if (context == null) {
throw new IllegalArgumentException("context shouldn't be null");
}
@@ -370,12 +361,12 @@
* @throws IllegalArgumentException if id is {@code null}
* @return
*/
- public Builder setId(@NonNull String id) {
+ public T setId(@NonNull String id) {
if (id == null) {
throw new IllegalArgumentException("id shouldn't be null");
}
mId = id;
- return this;
+ return (T) this;
}
/**
@@ -384,9 +375,9 @@
* @param callback session callback.
* @return
*/
- public Builder setSessionCallback(@Nullable SessionCallback callback) {
+ public T setSessionCallback(@Nullable C callback) {
mCallback = callback;
- return this;
+ return (T) this;
}
/**
@@ -396,6 +387,24 @@
* @throws IllegalStateException if the session with the same id is already exists for the
* package.
*/
+ public abstract MediaSession2 build() throws IllegalStateException;
+ }
+
+ /**
+ * Builder for {@link MediaSession2}.
+ * <p>
+ * Any incoming event from the {@link MediaController2} will be handled on the thread
+ * that created session with the {@link Builder#build()}.
+ */
+ // TODO(jaewan): Move this to updatable
+ // TODO(jaewan): Add setRatingType()
+ // TODO(jaewan): Add setSessionActivity()
+ public static final class Builder extends BuilderBase<Builder, SessionCallback> {
+ public Builder(Context context, @NonNull MediaPlayerBase player) {
+ super(context, player);
+ }
+
+ @Override
public MediaSession2 build() throws IllegalStateException {
if (mCallback == null) {
mCallback = new SessionCallback();
@@ -492,7 +501,7 @@
* framework had to add heuristics to figure out if an app is
* @hide
*/
- private MediaSession2(Context context, MediaPlayerBase player, String id,
+ MediaSession2(Context context, MediaPlayerBase player, String id,
SessionCallback callback) {
super();
mProvider = ApiLoader.getProvider(context)
diff --git a/media/java/android/media/MediaSessionService2.java b/media/java/android/media/MediaSessionService2.java
index f1f5467..1a12d68 100644
--- a/media/java/android/media/MediaSessionService2.java
+++ b/media/java/android/media/MediaSessionService2.java
@@ -29,7 +29,7 @@
import android.os.IBinder;
/**
- * Service version of the {@link MediaSession2}.
+ * Base class for media session services, which is the service version of the {@link MediaSession2}.
* <p>
* It's highly recommended for an app to use this instead of {@link MediaSession2} if it wants
* to keep media playback in the background.
@@ -43,11 +43,11 @@
* </ul>
* For example, user's voice command can start playback of your app even when it's not running.
* <p>
- * To use this class, adding followings directly to your {@code AndroidManifest.xml}.
+ * To extend this class, adding followings directly to your {@code AndroidManifest.xml}.
* <pre>
* <service android:name="component_name_of_your_implementation" >
* <intent-filter>
- * <action android:name="android.media.session.MediaSessionService2" />
+ * <action android:name="android.media.MediaSessionService2" />
* </intent-filter>
* </service></pre>
* <p>
@@ -58,7 +58,7 @@
* <pre>
* <service android:name="component_name_of_your_implementation" >
* <intent-filter>
- * <action android:name="android.media.session.MediaSessionService2" />
+ * <action android:name="android.media.MediaSessionService2" />
* </intent-filter>
* <meta-data android:name="android.media.session"
* android:value="session_id"/>
@@ -120,8 +120,7 @@
* This is the interface name that a service implementing a session service should say that it
* support -- that is, this is the action it uses for its intent filter.
*/
- public static final String SERVICE_INTERFACE =
- "android.media.session.MediaSessionService2";
+ public static final String SERVICE_INTERFACE = "android.media.MediaSessionService2";
/**
* Name under which a MediaSessionService2 component publishes information about itself.
@@ -129,21 +128,13 @@
*/
public static final String SERVICE_META_DATA = "android.media.session";
- /**
- * Default notification channel ID used by {@link #onUpdateNotification(PlaybackState)}.
- *
- */
- public static final String DEFAULT_MEDIA_NOTIFICATION_CHANNEL_ID = "media_session_service";
-
- /**
- * Default notification channel ID used by {@link #onUpdateNotification(PlaybackState)}.
- *
- */
- public static final int DEFAULT_MEDIA_NOTIFICATION_ID = 1001;
-
public MediaSessionService2() {
super();
- mProvider = ApiLoader.getProvider(this).createMediaSessionService2(this);
+ mProvider = createProvider();
+ }
+
+ MediaSessionService2Provider createProvider() {
+ return ApiLoader.getProvider(this).createMediaSessionService2(this);
}
/**
@@ -168,32 +159,24 @@
* Service wouldn't run if {@code null} is returned or session's ID doesn't match with the
* expected ID that you've specified through the AndroidManifest.xml.
* <p>
- * This method will be call on the main thread.
+ * This method will be called on the main thread.
*
* @param sessionId session id written in the AndroidManifest.xml.
* @return a new session
* @see MediaSession2.Builder
* @see #getSession()
*/
- // TODO(jaewan): Replace this with onCreateSession(). Its sesssion callback will replace
- // this abstract method.
- // TODO(jaewan): Should we also include/documents notification listener access?
- // TODO(jaewan): Is term accepted/rejected correct? For permission, granted is more common.
- // TODO(jaewan): Return ConnectResult() that encapsulate supported action and player.
public @NonNull abstract MediaSession2 onCreateSession(String sessionId);
/**
* Called when the playback state of this session is changed, and notification needs update.
- * <p>
- * Default media style notification will be shown if you don't override this or return
- * {@code null}. Override this method to show your own notification UI.
+ * Override this method to show your own notification UI.
* <p>
* With the notification returned here, the service become foreground service when the playback
* is started. It becomes background service after the playback is stopped.
*
* @param state playback state
- * @return a {@link MediaNotification}. If it's {@code null}, default notification will be shown
- * instead.
+ * @return a {@link MediaNotification}. If it's {@code null}, notification wouldn't be shown.
*/
// TODO(jaewan): Also add metadata
public MediaNotification onUpdateNotification(PlaybackState state) {
diff --git a/media/java/android/media/SessionToken.java b/media/java/android/media/SessionToken.java
index b470dea..53fc24a 100644
--- a/media/java/android/media/SessionToken.java
+++ b/media/java/android/media/SessionToken.java
@@ -42,12 +42,13 @@
// TODO(jaewan): Find better name for this (SessionToken or Session2Token)
public final class SessionToken {
@Retention(RetentionPolicy.SOURCE)
- @IntDef(value = {TYPE_SESSION, TYPE_SESSION_SERVICE})
+ @IntDef(value = {TYPE_SESSION, TYPE_SESSION_SERVICE, TYPE_LIBRARY_SERVICE})
public @interface TokenType {
}
public static final int TYPE_SESSION = 0;
public static final int TYPE_SESSION_SERVICE = 1;
+ public static final int TYPE_LIBRARY_SERVICE = 2;
private static final String KEY_TYPE = "android.media.token.type";
private static final String KEY_PACKAGE_NAME = "android.media.token.package_name";
@@ -73,6 +74,7 @@
* @hide
*/
// TODO(jaewan): UID is also needed.
+ // TODO(jaewan): Unhide
public SessionToken(@TokenType int type, @NonNull String packageName, @NonNull String id,
@Nullable String serviceName, @Nullable IMediaSession2 sessionBinder) {
// TODO(jaewan): Add sanity check.
diff --git a/media/java/android/media/update/MediaBrowser2Provider.java b/media/java/android/media/update/MediaBrowser2Provider.java
new file mode 100644
index 0000000..355dbc9
--- /dev/null
+++ b/media/java/android/media/update/MediaBrowser2Provider.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 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.media.update;
+
+import android.os.Bundle;
+
+/**
+ * @hide
+ */
+public interface MediaBrowser2Provider extends MediaController2Provider {
+ void getBrowserRoot_impl(Bundle rootHints);
+}
diff --git a/media/java/android/media/update/MediaControlView2Provider.java b/media/java/android/media/update/MediaControlView2Provider.java
index 83763b4..b3719c8 100644
--- a/media/java/android/media/update/MediaControlView2Provider.java
+++ b/media/java/android/media/update/MediaControlView2Provider.java
@@ -40,7 +40,6 @@
void show_impl(int timeout);
boolean isShowing_impl();
void hide_impl();
- void showCCButton_impl();
boolean isPlaying_impl();
int getCurrentPosition_impl();
int getBufferPercentage_impl();
diff --git a/media/java/android/media/update/MediaLibraryService2Provider.java b/media/java/android/media/update/MediaLibraryService2Provider.java
new file mode 100644
index 0000000..7e3444f
--- /dev/null
+++ b/media/java/android/media/update/MediaLibraryService2Provider.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 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.media.update;
+
+/**
+ * @hide
+ */
+public interface MediaLibraryService2Provider extends MediaSessionService2Provider {
+ // Nothing new for now
+}
diff --git a/media/java/android/media/update/StaticProvider.java b/media/java/android/media/update/StaticProvider.java
index 91c9c66..fef2cbb 100644
--- a/media/java/android/media/update/StaticProvider.java
+++ b/media/java/android/media/update/StaticProvider.java
@@ -19,7 +19,11 @@
import android.annotation.Nullable;
import android.content.Context;
import android.media.IMediaSession2Callback;
+import android.media.MediaBrowser2;
+import android.media.MediaBrowser2.BrowserCallback;
import android.media.MediaController2;
+import android.media.MediaController2.ControllerCallback;
+import android.media.MediaLibraryService2;
import android.media.MediaPlayerBase;
import android.media.MediaSession2;
import android.media.MediaSessionService2;
@@ -53,7 +57,12 @@
String packageName, IMediaSession2Callback callback);
MediaController2Provider createMediaController2(
MediaController2 instance, Context context, SessionToken token,
- MediaController2.ControllerCallback callback, Executor executor);
+ ControllerCallback callback, Executor executor);
+ MediaBrowser2Provider createMediaBrowser2(
+ MediaBrowser2 instance, Context context, SessionToken token,
+ BrowserCallback callback, Executor executor);
MediaSessionService2Provider createMediaSessionService2(
MediaSessionService2 instance);
+ MediaSessionService2Provider createMediaLibraryService2(
+ MediaLibraryService2 instance);
}
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index 1dddbee..bbed93d 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -19,6 +19,7 @@
#include <utils/Log.h>
#include "android_media_MediaDrm.h"
+#include "android_media_MediaMetricsJNI.h"
#include "android_runtime/AndroidRuntime.h"
#include "android_runtime/Log.h"
@@ -1603,6 +1604,31 @@
return match;
}
+static jobject
+android_media_MediaDrm_native_getMetrics(JNIEnv *env, jobject thiz)
+{
+ sp<IDrm> drm = GetDrm(env, thiz);
+ if (drm == NULL ) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "MediaDrm obj is null");
+ return NULL;
+ }
+
+ // Retrieve current metrics snapshot from drm.
+ MediaAnalyticsItem item ;
+ status_t err = drm->getMetrics(&item);
+ if (err != OK) {
+ ALOGE("getMetrics failed: %d", (int)err);
+ return (jobject) NULL;
+ }
+
+ jobject mybundle = MediaMetricsJNI::writeMetricsToBundle(env, &item, NULL);
+ if (mybundle == NULL) {
+ ALOGE("getMetrics metric conversion failed");
+ }
+
+ return mybundle;
+}
static jbyteArray android_media_MediaDrm_signRSANative(
JNIEnv *env, jobject /* thiz */, jobject jdrm, jbyteArray jsessionId,
@@ -1739,6 +1765,9 @@
{ "signRSANative", "(Landroid/media/MediaDrm;[BLjava/lang/String;[B[B)[B",
(void *)android_media_MediaDrm_signRSANative },
+
+ { "getMetricsNative", "()Landroid/os/PersistableBundle;",
+ (void *)android_media_MediaDrm_native_getMetrics },
};
int register_android_media_Drm(JNIEnv *env) {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 2a697b8..5435e11 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -2892,11 +2892,14 @@
for (int i = 0; i < users.size(); i++) {
final int userId = users.get(i).id;
+ // Do we have to increment the generation for users that are not running?
+ // Yeah let's assume so...
+ final int key = makeKey(SETTINGS_TYPE_SECURE, userId);
+ mGenerationRegistry.incrementGeneration(key);
+
if (!mUserManager.isUserRunning(UserHandle.of(userId))) {
continue;
}
-
- final int key = makeKey(SETTINGS_TYPE_GLOBAL, userId);
final Uri uri = getNotificationUriFor(key, Secure.LOCATION_PROVIDERS_ALLOWED);
mHandler.obtainMessage(MyHandler.MSG_NOTIFY_URI_CHANGED,
diff --git a/packages/SystemUI/Android.mk b/packages/SystemUI/Android.mk
index 73fcdd7..251ae2d 100644
--- a/packages/SystemUI/Android.mk
+++ b/packages/SystemUI/Android.mk
@@ -27,7 +27,12 @@
LOCAL_MODULE_TAGS := optional
-LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-Iaidl-files-under, src)
+RELATIVE_FINGERPRINT_PATH := ../../core/java/android/hardware/fingerprint
+
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under, src) \
+ $(call all-Iaidl-files-under, src) \
+ $(call all-Iaidl-files-under, $(RELATIVE_FINGERPRINT_PATH))
LOCAL_STATIC_ANDROID_LIBRARIES := \
SystemUIPluginLib \
diff --git a/packages/SystemUI/res/drawable/fingerprint_dialog_bg.xml b/packages/SystemUI/res/drawable/fingerprint_dialog_bg.xml
new file mode 100644
index 0000000..4a77af9
--- /dev/null
+++ b/packages/SystemUI/res/drawable/fingerprint_dialog_bg.xml
@@ -0,0 +1,25 @@
+<?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
+ -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="@color/fingerprint_dialog_bg_color" />
+ <corners android:radius="1dp"
+ android:topLeftRadius="16dp"
+ android:topRightRadius="16dp"
+ android:bottomLeftRadius="0dp"
+ android:bottomRightRadius="0dp"/>
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/fingerprint_icon.xml b/packages/SystemUI/res/drawable/fingerprint_icon.xml
new file mode 100644
index 0000000..76a86ae
--- /dev/null
+++ b/packages/SystemUI/res/drawable/fingerprint_icon.xml
@@ -0,0 +1,107 @@
+<?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
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="60dp"
+ android:height="60dp"
+ android:viewportWidth="60"
+ android:viewportHeight="60">
+
+ <path
+ android:fillColor="#1A73E8"
+ android:fillType="evenOdd"
+ android:strokeWidth="1"
+ android:pathData="M 30 0 C 46.5685424949 0 60 13.4314575051 60 30 C 60 46.5685424949 46.5685424949 60 30 60 C 13.4314575051 60 0 46.5685424949 0 30 C 0 13.4314575051 13.4314575051 0 30 0 Z" />
+ <group
+ android:translateX="17.727273"
+ android:translateY="16.363636">
+ <path
+ android:fillColor="#FFFFFF"
+ android:strokeWidth="1"
+ android:pathData="M20.3065726,3.44516129 C20.1974817,3.44516129 20.0883908,3.41788856
+19.9929362,3.36334311 C17.3747544,2.01334311 15.111118,1.44061584
+12.3974817,1.44061584 C9.69748166,1.44061584 7.1338453,2.08152493
+4.80202711,3.36334311 C4.47475439,3.54061584 4.06566348,3.41788856
+3.87475439,3.09061584 C3.69748166,2.76334311 3.82020893,2.34061584
+4.14748166,2.16334311 C6.6838453,0.786070381 9.46566348,0.0769794721
+12.3974817,0.0769794721 C15.3020271,0.0769794721 17.8383908,0.717888563
+20.6202089,2.14970674 C20.961118,2.32697947 21.0838453,2.73607038
+20.9065726,3.06334311 C20.7838453,3.30879765 20.5520271,3.44516129
+20.3065726,3.44516129 L20.3065726,3.44516129 Z M0.792936205,10.6042522
+C0.656572568,10.6042522 0.520208932,10.5633431 0.397481659,10.4815249
+C0.0838452956,10.2633431 0.0156634774,9.84061584 0.233845296,9.52697947
+C1.5838453,7.61788856 3.30202711,6.11788856 5.34748166,5.06788856
+C9.62929984,2.85879765 15.111118,2.84516129 19.4065726,5.0542522
+C21.4520271,6.1042522 23.1702089,7.59061584 24.5202089,9.48607038
+C24.7383908,9.78607038 24.6702089,10.222434 24.3565726,10.4406158
+C24.0429362,10.6587977 23.6202089,10.5906158 23.4020271,10.2769795
+C22.1747544,8.55879765 20.6202089,7.20879765 18.7792998,6.26788856
+C14.8656635,4.26334311 9.86111802,4.26334311 5.96111802,6.28152493
+C4.10657257,7.23607038 2.55202711,8.59970674 1.32475439,10.3178886
+C1.21566348,10.5087977 1.01111802,10.6042522 0.792936205,10.6042522
+L0.792936205,10.6042522 Z M9.31566348,27.0633431 C9.13839075,27.0633431
+8.96111802,26.9951613 8.83839075,26.8587977 C7.65202711,25.672434
+7.01111802,24.9087977 6.09748166,23.2587977 C5.15657257,21.5815249
+4.66566348,19.5360704 4.66566348,17.3406158 C4.66566348,13.2906158
+8.12929984,9.99061584 12.3838453,9.99061584 C16.6383908,9.99061584
+20.1020271,13.2906158 20.1020271,17.3406158 C20.1020271,17.722434
+19.8020271,18.022434 19.4202089,18.022434 C19.0383908,18.022434
+18.7383908,17.722434 18.7383908,17.3406158 C18.7383908,14.0406158
+15.8883908,11.3542522 12.3838453,11.3542522 C8.87929984,11.3542522
+6.02929984,14.0406158 6.02929984,17.3406158 C6.02929984,19.3042522
+6.46566348,21.1178886 7.29748166,22.5906158 C8.17020893,24.1587977
+8.77020893,24.8269795 9.82020893,25.8906158 C10.0792998,26.1633431
+10.0792998,26.5860704 9.82020893,26.8587977 C9.67020893,26.9951613
+9.4929362,27.0633431 9.31566348,27.0633431 Z M19.0929362,24.5406158
+C17.4702089,24.5406158 16.0383908,24.1315249 14.8656635,23.3269795
+C12.8338453,21.9497067 11.6202089,19.7133431 11.6202089,17.3406158
+C11.6202089,16.9587977 11.9202089,16.6587977 12.3020271,16.6587977
+C12.6838453,16.6587977 12.9838453,16.9587977 12.9838453,17.3406158
+C12.9838453,19.2633431 13.9656635,21.0769795 15.6292998,22.1951613
+C16.5974817,22.8497067 17.7292998,23.1633431 19.0929362,23.1633431
+C19.4202089,23.1633431 19.9656635,23.122434 20.511118,23.0269795
+C20.8792998,22.9587977 21.2338453,23.2042522 21.3020271,23.5860704
+C21.3702089,23.9542522 21.1247544,24.3087977 20.7429362,24.3769795
+C19.9656635,24.5269795 19.2838453,24.5406158 19.0929362,24.5406158
+L19.0929362,24.5406158 Z M16.3520271,27.3497067 C16.2974817,27.3497067
+16.2292998,27.3360704 16.1747544,27.322434 C14.0065726,26.722434
+12.5883908,25.9178886 11.1020271,24.4587977 C9.1929362,22.5633431
+8.1429362,20.0406158 8.1429362,17.3406158 C8.1429362,15.1315249
+10.0247544,13.3315249 12.3429362,13.3315249 C14.661118,13.3315249
+16.5429362,15.1315249 16.5429362,17.3406158 C16.5429362,18.7997067
+17.811118,19.9860704 19.3792998,19.9860704 C20.9474817,19.9860704
+22.2156635,18.7997067 22.2156635,17.3406158 C22.2156635,12.1997067
+17.7838453,8.02697947 12.3292998,8.02697947 C8.45657257,8.02697947
+4.91111802,10.1815249 3.31566348,13.522434 C2.7838453,14.6269795
+2.51111802,15.922434 2.51111802,17.3406158 C2.51111802,18.4042522
+2.60657257,20.0815249 3.42475439,22.2633431 C3.56111802,22.6178886
+3.3838453,23.0133431 3.02929984,23.1360704 C2.67475439,23.272434
+2.27929984,23.0815249 2.15657257,22.7406158 C1.48839075,20.9542522
+1.16111802,19.1815249 1.16111802,17.3406158 C1.16111802,15.7042522
+1.47475439,14.2178886 2.08839075,12.922434 C3.90202711,9.11788856
+7.92475439,6.64970674 12.3292998,6.64970674 C18.5338453,6.64970674
+23.5792998,11.4360704 23.5792998,17.3269795 C23.5792998,19.5360704
+21.6974817,21.3360704 19.3792998,21.3360704 C17.061118,21.3360704
+15.1792998,19.5360704 15.1792998,17.3269795 C15.1792998,15.8678886
+13.911118,14.6815249 12.3429362,14.6815249 C10.7747544,14.6815249
+9.50657257,15.8678886 9.50657257,17.3269795 C9.50657257,19.6587977
+10.4065726,21.8406158 12.0565726,23.4769795 C13.3520271,24.7587977
+14.5929362,25.4678886 16.5156635,25.9997067 C16.8838453,26.0951613
+17.0883908,26.4769795 16.9929362,26.8315249 C16.9247544,27.1451613
+16.6383908,27.3497067 16.3520271,27.3497067 L16.3520271,27.3497067 Z" />
+ </group>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/fingerprint_dialog.xml b/packages/SystemUI/res/layout/fingerprint_dialog.xml
new file mode 100644
index 0000000..e5f62b3
--- /dev/null
+++ b/packages/SystemUI/res/layout/fingerprint_dialog.xml
@@ -0,0 +1,163 @@
+<?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
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="bottom"
+ android:background="@color/fingerprint_dialog_dim_color"
+ android:orientation="vertical">
+
+ <!-- This is not a Space since Spaces cannot be clicked -->
+ <View
+ android:id="@+id/space"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_weight="1" />
+
+ <LinearLayout
+ android:id="@+id/dialog"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:elevation="2dp"
+ android:background="@drawable/fingerprint_dialog_bg">
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:elevation="2dp">
+
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="@dimen/fingerprint_dialog_icon_size"
+ android:layout_height="@dimen/fingerprint_dialog_icon_size"
+ android:layout_marginTop="16dp"
+ android:layout_marginStart="16dp"
+ android:scaleType="centerInside" />
+
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_toRightOf="@+id/icon"
+ android:layout_marginEnd="16dp"
+ android:layout_marginStart="16dp"
+ android:layout_marginTop="16dp"
+ android:textSize="20sp"
+ android:maxLines="1"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:marqueeRepeatLimit="marquee_forever"
+ android:textColor="@color/fingerprint_dialog_text_color"/>
+
+ <TextView
+ android:id="@+id/subtitle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_toRightOf="@+id/icon"
+ android:layout_below="@+id/title"
+ android:layout_marginEnd="16dp"
+ android:layout_marginStart="16dp"
+ android:layout_marginTop="4dp"
+ android:textSize="12sp"
+ android:maxLines="2"
+ android:textColor="@color/fingerprint_dialog_text_color"/>
+
+ </RelativeLayout>
+
+ <TextView
+ android:id="@+id/description"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="16dp"
+ android:layout_marginStart="16dp"
+ android:paddingTop="16dp"
+ android:paddingBottom="20dp"
+ android:textSize="16sp"
+ android:maxLines="4"
+ android:textColor="@color/fingerprint_dialog_text_color"/>
+
+ <ImageView
+ android:id="@+id/fingerprint_icon"
+ android:layout_width="@dimen/fingerprint_dialog_fp_icon_size"
+ android:layout_height="@dimen/fingerprint_dialog_fp_icon_size"
+ android:layout_gravity="center_horizontal"
+ android:scaleType="centerInside"
+ android:src="@drawable/fingerprint_icon"/>
+
+ <TextView
+ android:id="@+id/error"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd = "16dp"
+ android:layout_marginStart="16dp"
+ android:paddingTop="16dp"
+ android:paddingBottom="60dp"
+ android:textSize="12sp"
+ android:visibility="invisible"
+ android:gravity="center_horizontal"
+ android:textColor="@color/fingerprint_error_message_color"/>
+
+ <LinearLayout android:id="@+id/buttonPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="54dip"
+ android:orientation="vertical" >
+ <LinearLayout
+ style="?android:attr/buttonBarStyle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:paddingTop="4dip"
+ android:paddingStart="2dip"
+ android:paddingEnd="2dip"
+ android:measureWithLargestChild="true">
+ <LinearLayout android:id="@+id/leftSpacer"
+ android:layout_weight="0.25"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:visibility="gone" />
+ <!-- Positive Button -->
+ <Button android:id="@+id/button1"
+ android:layout_width="0dip"
+ android:layout_gravity="start"
+ android:layout_weight="1"
+ style="?android:attr/buttonBarButtonStyle"
+ android:maxLines="2"
+ android:layout_height="wrap_content"/>
+ <!-- Negative Button -->
+ <Button android:id="@+id/button2"
+ android:layout_width="0dip"
+ android:layout_gravity="end"
+ android:layout_weight="1"
+ style="?android:attr/buttonBarButtonStyle"
+ android:maxLines="2"
+ android:layout_height="wrap_content" />
+ <LinearLayout android:id="@+id/rightSpacer"
+ android:layout_width="0dip"
+ android:layout_weight="0.25"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:visibility="gone" />
+ </LinearLayout>
+ </LinearLayout>
+ </LinearLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 0f4c3b8..4fcfdf7 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -156,6 +156,14 @@
<color name="zen_introduction">#ffffffff</color>
+
<color name="smart_reply_button_text">#ff4285f4</color><!-- blue 500 -->
<color name="smart_reply_button_background">#fff7f7f7</color>
+
+ <!-- Fingerprint dialog colors -->
+ <color name="fingerprint_dialog_bg_color">#f4ffffff</color> <!-- 96% white -->
+ <color name="fingerprint_dialog_text_color">#ff424242</color> <!-- gray 800-->
+ <color name="fingerprint_dialog_dim_color">#80000000</color> <!-- 50% black -->
+ <color name="fingerprint_error_message_color">#ff5722</color>
+
</resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index b33f857..6768470 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -350,6 +350,7 @@
<item>com.android.systemui.globalactions.GlobalActionsComponent</item>
<item>com.android.systemui.RoundedCorners</item>
<item>com.android.systemui.EmulatedDisplayCutout</item>
+ <item>com.android.systemui.fingerprint.FingerprintDialogImpl</item>
</string-array>
<!-- SystemUI vender service, used in config_systemUIServiceComponents. -->
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 887d3cb..b749a18 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -579,7 +579,7 @@
<dimen name="keyguard_affordance_icon_width">24dp</dimen>
<dimen name="keyguard_indication_margin_bottom">65dp</dimen>
- <dimen name="keyguard_indication_margin_bottom_ambient">30dp</dimen>
+ <dimen name="keyguard_indication_margin_bottom_ambient">16dp</dimen>
<!-- The text size for battery level -->
<dimen name="battery_level_text_size">12sp</dimen>
@@ -889,4 +889,8 @@
<dimen name="smart_reply_button_spacing">8dp</dimen>
<dimen name="smart_reply_button_padding_vertical">4dp</dimen>
<dimen name="smart_reply_button_font_size">14sp</dimen>
+
+ <dimen name="fingerprint_dialog_icon_size">44dp</dimen>
+ <dimen name="fingerprint_dialog_fp_icon_size">60dp</dimen>
+ <dimen name="fingerprint_dialog_animation_translation_offset">350dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 8d6b38f..edf19fd 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -764,6 +764,11 @@
<string name="quick_settings_tethering_label">Tethering</string>
<!-- QuickSettings: Hotspot. [CHAR LIMIT=NONE] -->
<string name="quick_settings_hotspot_label">Hotspot</string>
+ <!-- QuickSettings: Hotspot: Secondary label for how many devices are connected to the hotspot [CHAR LIMIT=NONE] -->
+ <plurals name="quick_settings_hotspot_num_devices">
+ <item quantity="one">%d device</item>
+ <item quantity="other">%d devices</item>
+ </plurals>
<!-- QuickSettings: Notifications [CHAR LIMIT=NONE] -->
<string name="quick_settings_notifications_label">Notifications</string>
<!-- QuickSettings: Flashlight [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 19afcf5..9e4b405 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -1257,7 +1257,8 @@
mFingerprintCancelSignal.cancel();
}
mFingerprintCancelSignal = new CancellationSignal();
- mFpm.authenticate(null, mFingerprintCancelSignal, 0, mAuthenticationCallback, null, userId);
+ mFpm.authenticate(null, mFingerprintCancelSignal, 0, mAuthenticationCallback, null,
+ userId);
setFingerprintRunningState(FINGERPRINT_STATE_RUNNING);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogImpl.java b/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogImpl.java
new file mode 100644
index 0000000..262c71a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogImpl.java
@@ -0,0 +1,220 @@
+/*
+ * 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.systemui.fingerprint;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.hardware.fingerprint.FingerprintDialog;
+import android.hardware.fingerprint.IFingerprintDialogReceiver;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.WindowManager;
+
+import com.android.internal.os.SomeArgs;
+import com.android.systemui.SystemUI;
+import com.android.systemui.statusbar.CommandQueue;
+
+public class FingerprintDialogImpl extends SystemUI implements CommandQueue.Callbacks {
+ private static final String TAG = "FingerprintDialogImpl";
+ private static final boolean DEBUG = true;
+
+ protected static final int MSG_SHOW_DIALOG = 1;
+ protected static final int MSG_FINGERPRINT_AUTHENTICATED = 2;
+ protected static final int MSG_FINGERPRINT_HELP = 3;
+ protected static final int MSG_FINGERPRINT_ERROR = 4;
+ protected static final int MSG_HIDE_DIALOG = 5;
+ protected static final int MSG_BUTTON_NEGATIVE = 6;
+ protected static final int MSG_USER_CANCELED = 7;
+ protected static final int MSG_BUTTON_POSITIVE = 8;
+ protected static final int MSG_CLEAR_MESSAGE = 9;
+
+
+ private FingerprintDialogView mDialogView;
+ private WindowManager mWindowManager;
+ private IFingerprintDialogReceiver mReceiver;
+ private boolean mDialogShowing;
+
+ private Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch(msg.what) {
+ case MSG_SHOW_DIALOG:
+ handleShowDialog((SomeArgs) msg.obj);
+ break;
+ case MSG_FINGERPRINT_AUTHENTICATED:
+ handleFingerprintAuthenticated();
+ break;
+ case MSG_FINGERPRINT_HELP:
+ handleFingerprintHelp((String) msg.obj);
+ break;
+ case MSG_FINGERPRINT_ERROR:
+ handleFingerprintError((String) msg.obj);
+ break;
+ case MSG_HIDE_DIALOG:
+ handleHideDialog((Boolean) msg.obj);
+ break;
+ case MSG_BUTTON_NEGATIVE:
+ handleButtonNegative();
+ break;
+ case MSG_USER_CANCELED:
+ handleUserCanceled();
+ break;
+ case MSG_BUTTON_POSITIVE:
+ handleButtonPositive();
+ break;
+ case MSG_CLEAR_MESSAGE:
+ handleClearMessage();
+ break;
+ }
+ }
+ };
+
+ @Override
+ public void start() {
+ if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
+ return;
+ }
+ getComponent(CommandQueue.class).addCallbacks(this);
+ mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
+ mDialogView = new FingerprintDialogView(mContext, mHandler);
+ }
+
+ @Override
+ public void showFingerprintDialog(Bundle bundle, IFingerprintDialogReceiver receiver) {
+ if (DEBUG) Log.d(TAG, "showFingerprintDialog");
+ // Remove these messages as they are part of the previous client
+ mHandler.removeMessages(MSG_FINGERPRINT_ERROR);
+ mHandler.removeMessages(MSG_FINGERPRINT_HELP);
+ mHandler.removeMessages(MSG_FINGERPRINT_AUTHENTICATED);
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = bundle;
+ args.arg2 = receiver;
+ mHandler.obtainMessage(MSG_SHOW_DIALOG, args).sendToTarget();
+ }
+
+ @Override
+ public void onFingerprintAuthenticated() {
+ if (DEBUG) Log.d(TAG, "onFingerprintAuthenticated");
+ mHandler.obtainMessage(MSG_FINGERPRINT_AUTHENTICATED).sendToTarget();
+ }
+
+ @Override
+ public void onFingerprintHelp(String message) {
+ if (DEBUG) Log.d(TAG, "onFingerprintHelp: " + message);
+ mHandler.obtainMessage(MSG_FINGERPRINT_HELP, message).sendToTarget();
+ }
+
+ @Override
+ public void onFingerprintError(String error) {
+ if (DEBUG) Log.d(TAG, "onFingerprintError: " + error);
+ mHandler.obtainMessage(MSG_FINGERPRINT_ERROR, error).sendToTarget();
+ }
+
+ @Override
+ public void hideFingerprintDialog() {
+ if (DEBUG) Log.d(TAG, "hideFingerprintDialog");
+ mHandler.obtainMessage(MSG_HIDE_DIALOG, false /* userCanceled */).sendToTarget();
+ }
+
+ private void handleShowDialog(SomeArgs args) {
+ if (DEBUG) Log.d(TAG, "handleShowDialog");
+ if (mDialogShowing) {
+ Log.w(TAG, "Dialog already showing");
+ return;
+ }
+ mReceiver = (IFingerprintDialogReceiver) args.arg2;
+ mDialogView.setBundle((Bundle)args.arg1);
+ mWindowManager.addView(mDialogView, mDialogView.getLayoutParams());
+ mDialogShowing = true;
+ }
+
+ private void handleFingerprintAuthenticated() {
+ if (DEBUG) Log.d(TAG, "handleFingerprintAuthenticated");
+ handleHideDialog(false /* userCanceled */);
+ }
+
+ private void handleFingerprintHelp(String message) {
+ if (DEBUG) Log.d(TAG, "handleFingerprintHelp: " + message);
+ mDialogView.showHelpMessage(message);
+ }
+
+ private void handleFingerprintError(String error) {
+ if (DEBUG) Log.d(TAG, "handleFingerprintError: " + error);
+ if (!mDialogShowing) {
+ if (DEBUG) Log.d(TAG, "Dialog already dismissed");
+ return;
+ }
+ mDialogView.showErrorMessage(error);
+ }
+
+ private void handleHideDialog(boolean userCanceled) {
+ if (DEBUG) Log.d(TAG, "handleHideDialog");
+ if (!mDialogShowing) {
+ // This can happen if there's a race and we get called from both
+ // onAuthenticated and onError, etc.
+ Log.w(TAG, "Dialog already dismissed, userCanceled: " + userCanceled);
+ return;
+ }
+ if (userCanceled) {
+ try {
+ mReceiver.onDialogDismissed(FingerprintDialog.DISMISSED_REASON_USER_CANCEL);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException when hiding dialog", e);
+ }
+ }
+ mReceiver = null;
+ mWindowManager.removeView(mDialogView);
+ mDialogShowing = false;
+ }
+
+ private void handleButtonNegative() {
+ if (mReceiver == null) {
+ Log.e(TAG, "Receiver is null");
+ return;
+ }
+ try {
+ mReceiver.onDialogDismissed(FingerprintDialog.DISMISSED_REASON_NEGATIVE);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Remote exception when handling negative button", e);
+ }
+ handleHideDialog(false /* userCanceled */);
+ }
+
+ private void handleButtonPositive() {
+ if (mReceiver == null) {
+ Log.e(TAG, "Receiver is null");
+ return;
+ }
+ try {
+ mReceiver.onDialogDismissed(FingerprintDialog.DISMISSED_REASON_POSITIVE);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Remote exception when handling positive button", e);
+ }
+ handleHideDialog(false /* userCanceled */);
+ }
+
+ private void handleClearMessage() {
+ mDialogView.clearMessage();
+ }
+
+ private void handleUserCanceled() {
+ handleHideDialog(true /* userCanceled */);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogView.java b/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogView.java
new file mode 100644
index 0000000..19bc2ec
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogView.java
@@ -0,0 +1,228 @@
+/*
+ * 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.systemui.fingerprint;
+
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.graphics.PixelFormat;
+import android.graphics.drawable.Drawable;
+import android.hardware.fingerprint.FingerprintDialog;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.view.animation.Interpolator;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.systemui.Interpolators;
+import com.android.systemui.R;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.PackageManagerWrapper;
+
+/**
+ * This class loads the view for the system-provided dialog. The view consists of:
+ * Application Icon, Title, Subtitle, Description, Fingerprint Icon, Error/Help message area,
+ * and positive/negative buttons.
+ */
+public class FingerprintDialogView extends LinearLayout {
+
+ private static final String TAG = "FingerprintDialogView";
+
+ private static final int ANIMATION_DURATION = 250; // ms
+
+ private final IBinder mWindowToken = new Binder();
+ private final ActivityManagerWrapper mActivityManagerWrapper;
+ private final PackageManagerWrapper mPackageManageWrapper;
+ private final Interpolator mLinearOutSlowIn;
+ private final Interpolator mFastOutLinearIn;
+ private final float mAnimationTranslationOffset;
+
+ private ViewGroup mLayout;
+ private final TextView mErrorText;
+ private Handler mHandler;
+ private Bundle mBundle;
+ private final LinearLayout mDialog;
+
+ public FingerprintDialogView(Context context, Handler handler) {
+ super(context);
+ mHandler = handler;
+ mActivityManagerWrapper = ActivityManagerWrapper.getInstance();
+ mPackageManageWrapper = PackageManagerWrapper.getInstance();
+ mLinearOutSlowIn = Interpolators.LINEAR_OUT_SLOW_IN;
+ mFastOutLinearIn = Interpolators.FAST_OUT_LINEAR_IN;
+ mAnimationTranslationOffset = getResources()
+ .getDimension(R.dimen.fingerprint_dialog_animation_translation_offset);
+
+ // Create the dialog
+ LayoutInflater factory = LayoutInflater.from(getContext());
+ mLayout = (ViewGroup) factory.inflate(R.layout.fingerprint_dialog, this, false);
+ addView(mLayout);
+
+ mDialog = mLayout.findViewById(R.id.dialog);
+
+ mErrorText = mLayout.findViewById(R.id.error);
+
+ mLayout.setOnKeyListener(new View.OnKeyListener() {
+ boolean downPressed = false;
+ @Override
+ public boolean onKey(View v, int keyCode, KeyEvent event) {
+ if (keyCode != KeyEvent.KEYCODE_BACK) {
+ return false;
+ }
+ if (event.getAction() == KeyEvent.ACTION_DOWN && downPressed == false) {
+ downPressed = true;
+ } else if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ downPressed = false;
+ } else if (event.getAction() == KeyEvent.ACTION_UP && downPressed == true) {
+ downPressed = false;
+ mHandler.obtainMessage(FingerprintDialogImpl.MSG_USER_CANCELED).sendToTarget();
+ }
+ return true;
+ }
+ });
+
+ final View space = mLayout.findViewById(R.id.space);
+ final Button negative = mLayout.findViewById(R.id.button2);
+ final Button positive = mLayout.findViewById(R.id.button1);
+
+ space.setClickable(true);
+ space.setOnTouchListener((View view, MotionEvent event) -> {
+ mHandler.obtainMessage(FingerprintDialogImpl.MSG_HIDE_DIALOG, true /* userCanceled*/)
+ .sendToTarget();
+ return true;
+ });
+
+ negative.setOnClickListener((View v) -> {
+ mHandler.obtainMessage(FingerprintDialogImpl.MSG_BUTTON_NEGATIVE).sendToTarget();
+ });
+
+ positive.setOnClickListener((View v) -> {
+ mHandler.obtainMessage(FingerprintDialogImpl.MSG_BUTTON_POSITIVE).sendToTarget();
+ });
+
+ mLayout.setFocusableInTouchMode(true);
+ mLayout.requestFocus();
+ }
+
+ @Override
+ public void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
+ final TextView title = mLayout.findViewById(R.id.title);
+ final TextView subtitle = mLayout.findViewById(R.id.subtitle);
+ final TextView description = mLayout.findViewById(R.id.description);
+ final Button negative = mLayout.findViewById(R.id.button2);
+ final ImageView image = mLayout.findViewById(R.id.icon);
+ final Button positive = mLayout.findViewById(R.id.button1);
+ final ImageView fingerprint_icon = mLayout.findViewById(R.id.fingerprint_icon);
+
+ title.setText(mBundle.getCharSequence(FingerprintDialog.KEY_TITLE));
+ title.setSelected(true);
+ subtitle.setText(mBundle.getCharSequence(FingerprintDialog.KEY_SUBTITLE));
+ description.setText(mBundle.getCharSequence(FingerprintDialog.KEY_DESCRIPTION));
+ negative.setText(mBundle.getCharSequence(FingerprintDialog.KEY_NEGATIVE_TEXT));
+ image.setImageDrawable(getAppIcon());
+
+ final CharSequence positiveText =
+ mBundle.getCharSequence(FingerprintDialog.KEY_POSITIVE_TEXT);
+ positive.setText(positiveText); // needs to be set for marquee to work
+ if (positiveText != null) {
+ positive.setVisibility(View.VISIBLE);
+ } else {
+ positive.setVisibility(View.GONE);
+ }
+
+ // Dim the background and slide the dialog up
+ mDialog.setTranslationY(mAnimationTranslationOffset);
+ mLayout.setAlpha(0f);
+ postOnAnimation(new Runnable() {
+ @Override
+ public void run() {
+ mLayout.animate()
+ .alpha(1f)
+ .setDuration(ANIMATION_DURATION)
+ .setInterpolator(mLinearOutSlowIn)
+ .withLayer()
+ .start();
+ mDialog.animate()
+ .translationY(0)
+ .setDuration(ANIMATION_DURATION)
+ .setInterpolator(mLinearOutSlowIn)
+ .withLayer()
+ .start();
+ }
+ });
+ }
+
+ public void setBundle(Bundle bundle) {
+ mBundle = bundle;
+ }
+
+ protected void clearMessage() {
+ mErrorText.setVisibility(View.INVISIBLE);
+ }
+
+ private void showMessage(String message) {
+ mHandler.removeMessages(FingerprintDialogImpl.MSG_CLEAR_MESSAGE);
+ mErrorText.setText(message);
+ mErrorText.setVisibility(View.VISIBLE);
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(FingerprintDialogImpl.MSG_CLEAR_MESSAGE),
+ FingerprintDialog.HIDE_DIALOG_DELAY);
+ }
+
+ public void showHelpMessage(String message) {
+ showMessage(message);
+ }
+
+ public void showErrorMessage(String error) {
+ showMessage(error);
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(FingerprintDialogImpl.MSG_HIDE_DIALOG,
+ false /* userCanceled */), FingerprintDialog.HIDE_DIALOG_DELAY);
+ }
+
+ private Drawable getAppIcon() {
+ final ActivityManager.RunningTaskInfo taskInfo = mActivityManagerWrapper.getRunningTask();
+ final ComponentName cn = taskInfo.topActivity;
+ final int userId = mActivityManagerWrapper.getCurrentUserId();
+ final ActivityInfo activityInfo = mPackageManageWrapper.getActivityInfo(cn, userId);
+ return mActivityManagerWrapper.getBadgedActivityIcon(activityInfo, userId);
+ }
+
+ public WindowManager.LayoutParams getLayoutParams() {
+ final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
+ WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
+ PixelFormat.TRANSLUCENT);
+ lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ lp.setTitle("FingerprintDialogView");
+ lp.token = mWindowToken;
+ return lp;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
index 910b6b1..e1b58fe 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
@@ -16,6 +16,7 @@
package com.android.systemui.qs.tiles;
+import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentFilter;
@@ -37,7 +38,7 @@
/** Quick settings tile: Hotspot **/
public class HotspotTile extends QSTileImpl<AirplaneBooleanState> {
static final Intent TETHER_SETTINGS = new Intent().setComponent(new ComponentName(
- "com.android.settings", "com.android.settings.TetherSettings"));
+ "com.android.settings", "com.android.settings.TetherSettings"));
private final Icon mEnabledStatic = ResourceIcon.get(R.drawable.ic_hotspot);
private final Icon mUnavailable = ResourceIcon.get(R.drawable.ic_hotspot_unavailable);
@@ -115,11 +116,19 @@
state.label = mContext.getString(R.string.quick_settings_hotspot_label);
checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_CONFIG_TETHERING);
- if (arg instanceof Boolean) {
- state.value = (boolean) arg;
+
+ final int numConnectedDevices;
+ if (arg instanceof CallbackInfo) {
+ CallbackInfo info = (CallbackInfo) arg;
+ state.value = info.enabled;
+ numConnectedDevices = info.numConnectedDevices;
} else {
state.value = mController.isHotspotEnabled();
+ numConnectedDevices = mController.getNumConnectedDevices();
}
+
+ state.secondaryLabel = getSecondaryLabel(state.value, numConnectedDevices);
+
state.icon = mEnabledStatic;
state.isAirplaneMode = mAirplaneMode.getValue() != 0;
state.isTransient = mController.isHotspotTransient();
@@ -133,6 +142,18 @@
: state.value || state.isTransient ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
}
+ @Nullable
+ private String getSecondaryLabel(boolean enabled, int numConnectedDevices) {
+ if (numConnectedDevices > 0 && enabled) {
+ return mContext.getResources().getQuantityString(
+ R.plurals.quick_settings_hotspot_num_devices,
+ numConnectedDevices,
+ numConnectedDevices);
+ }
+
+ return null;
+ }
+
@Override
public int getMetricsCategory() {
return MetricsEvent.QS_HOTSPOT;
@@ -148,9 +169,30 @@
}
private final class Callback implements HotspotController.Callback {
+ final CallbackInfo mCallbackInfo = new CallbackInfo();
+
@Override
- public void onHotspotChanged(boolean enabled) {
- refreshState(enabled);
+ public void onHotspotChanged(boolean enabled, int numConnectedDevices) {
+ mCallbackInfo.enabled = enabled;
+ mCallbackInfo.numConnectedDevices = numConnectedDevices;
+ refreshState(mCallbackInfo);
}
- };
+ }
+
+ /**
+ * Holder for any hotspot state info that needs to passed from the callback to
+ * {@link #handleUpdateState(State, Object)}.
+ */
+ protected static final class CallbackInfo {
+ boolean enabled;
+ int numConnectedDevices;
+
+ @Override
+ public String toString() {
+ return new StringBuilder("CallbackInfo[")
+ .append("enabled=").append(enabled)
+ .append(",numConnectedDevices=").append(numConnectedDevices)
+ .append(']').toString();
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index c6abcf2..00bc62e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -18,6 +18,7 @@
import android.content.ComponentName;
import android.graphics.Rect;
+import android.hardware.fingerprint.IFingerprintDialogReceiver;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -83,6 +84,11 @@
private static final int MSG_SHOW_SHUTDOWN_UI = 36 << MSG_SHIFT;
private static final int MSG_SET_TOP_APP_HIDES_STATUS_BAR = 37 << MSG_SHIFT;
private static final int MSG_ROTATION_PROPOSAL = 38 << MSG_SHIFT;
+ private static final int MSG_FINGERPRINT_SHOW = 39 << MSG_SHIFT;
+ private static final int MSG_FINGERPRINT_AUTHENTICATED = 40 << MSG_SHIFT;
+ private static final int MSG_FINGERPRINT_HELP = 41 << MSG_SHIFT;
+ private static final int MSG_FINGERPRINT_ERROR = 42 << MSG_SHIFT;
+ private static final int MSG_FINGERPRINT_HIDE = 43 << MSG_SHIFT;
public static final int FLAG_EXCLUDE_NONE = 0;
public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
@@ -145,6 +151,12 @@
default void handleShowShutdownUi(boolean isReboot, String reason) { }
default void onRotationProposal(int rotation, boolean isValid) { }
+
+ default void showFingerprintDialog(Bundle bundle, IFingerprintDialogReceiver receiver) { }
+ default void onFingerprintAuthenticated() { }
+ default void onFingerprintHelp(String message) { }
+ default void onFingerprintError(String error) { }
+ default void hideFingerprintDialog() { }
}
@VisibleForTesting
@@ -470,6 +482,45 @@
}
}
+ @Override
+ public void showFingerprintDialog(Bundle bundle, IFingerprintDialogReceiver receiver) {
+ synchronized (mLock) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = bundle;
+ args.arg2 = receiver;
+ mHandler.obtainMessage(MSG_FINGERPRINT_SHOW, args)
+ .sendToTarget();
+ }
+ }
+
+ @Override
+ public void onFingerprintAuthenticated() {
+ synchronized (mLock) {
+ mHandler.obtainMessage(MSG_FINGERPRINT_AUTHENTICATED).sendToTarget();
+ }
+ }
+
+ @Override
+ public void onFingerprintHelp(String message) {
+ synchronized (mLock) {
+ mHandler.obtainMessage(MSG_FINGERPRINT_HELP, message).sendToTarget();
+ }
+ }
+
+ @Override
+ public void onFingerprintError(String error) {
+ synchronized (mLock) {
+ mHandler.obtainMessage(MSG_FINGERPRINT_ERROR, error).sendToTarget();
+ }
+ }
+
+ @Override
+ public void hideFingerprintDialog() {
+ synchronized (mLock) {
+ mHandler.obtainMessage(MSG_FINGERPRINT_HIDE).sendToTarget();
+ }
+ }
+
private final class H extends Handler {
private H(Looper l) {
super(l);
@@ -671,6 +722,35 @@
mCallbacks.get(i).onRotationProposal(msg.arg1, msg.arg2 != 0);
}
break;
+ case MSG_FINGERPRINT_SHOW:
+ mHandler.removeMessages(MSG_FINGERPRINT_ERROR);
+ mHandler.removeMessages(MSG_FINGERPRINT_HELP);
+ mHandler.removeMessages(MSG_FINGERPRINT_AUTHENTICATED);
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ mCallbacks.get(i).showFingerprintDialog(
+ (Bundle)((SomeArgs)msg.obj).arg1,
+ (IFingerprintDialogReceiver)((SomeArgs)msg.obj).arg2);
+ }
+ break;
+ case MSG_FINGERPRINT_AUTHENTICATED:
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ mCallbacks.get(i).onFingerprintAuthenticated();
+ }
+ break;
+ case MSG_FINGERPRINT_HELP:
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ mCallbacks.get(i).onFingerprintHelp((String) msg.obj);
+ }
+ break;
+ case MSG_FINGERPRINT_ERROR:
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ mCallbacks.get(i).onFingerprintError((String) msg.obj);
+ }
+ break;
+ case MSG_FINGERPRINT_HIDE:
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ mCallbacks.get(i).hideFingerprintDialog();
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
index 149ec0b..36f9f6b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
@@ -127,7 +127,7 @@
private final HotspotController.Callback mHotspotCallback = new Callback() {
@Override
- public void onHotspotChanged(boolean enabled) {
+ public void onHotspotChanged(boolean enabled, int numDevices) {
if (mAutoTracker.isAdded(HOTSPOT)) return;
if (enabled) {
mHost.addTile(HOTSPOT);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 6857337..20b5018 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -665,7 +665,7 @@
private final HotspotController.Callback mHotspotCallback = new HotspotController.Callback() {
@Override
- public void onHotspotChanged(boolean enabled) {
+ public void onHotspotChanged(boolean enabled, int numDevices) {
mIconController.setIconVisibility(mSlotHotspot, enabled);
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java
index 6457209..830b50e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java
@@ -26,7 +26,9 @@
void setHotspotEnabled(boolean enabled);
boolean isHotspotSupported();
- public interface Callback {
- void onHotspotChanged(boolean enabled);
+ int getNumConnectedDevices();
+
+ interface Callback {
+ void onHotspotChanged(boolean enabled, int numDevices);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
index 1ebb986..8792b4f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
@@ -23,31 +23,35 @@
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.wifi.WifiManager;
-import android.os.Handler;
import android.os.UserManager;
import android.util.Log;
+import com.android.systemui.Dependency;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
-public class HotspotControllerImpl implements HotspotController {
+public class HotspotControllerImpl implements HotspotController, WifiManager.SoftApCallback {
private static final String TAG = "HotspotController";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
- private final Receiver mReceiver = new Receiver();
+ private final ArrayList<Callback> mCallbacks = new ArrayList<>();
+ private final WifiStateReceiver mWifiStateReceiver = new WifiStateReceiver();
private final ConnectivityManager mConnectivityManager;
+ private final WifiManager mWifiManager;
private final Context mContext;
private int mHotspotState;
+ private int mNumConnectedDevices;
private boolean mWaitingForCallback;
public HotspotControllerImpl(Context context) {
mContext = context;
- mConnectivityManager = (ConnectivityManager) context.getSystemService(
- Context.CONNECTIVITY_SERVICE);
+ mConnectivityManager =
+ (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+ mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
}
@Override
@@ -84,7 +88,8 @@
if (callback == null || mCallbacks.contains(callback)) return;
if (DEBUG) Log.d(TAG, "addCallback " + callback);
mCallbacks.add(callback);
- mReceiver.setListening(!mCallbacks.isEmpty());
+
+ updateWifiStateListeners(!mCallbacks.isEmpty());
}
}
@@ -94,7 +99,26 @@
if (DEBUG) Log.d(TAG, "removeCallback " + callback);
synchronized (mCallbacks) {
mCallbacks.remove(callback);
- mReceiver.setListening(!mCallbacks.isEmpty());
+
+ updateWifiStateListeners(!mCallbacks.isEmpty());
+ }
+ }
+
+ /**
+ * Updates the wifi state receiver to either start or stop listening to get updates to the
+ * hotspot status. Additionally starts listening to wifi manager state to track the number of
+ * connected devices.
+ *
+ * @param shouldListen whether we should start listening to various wifi statuses
+ */
+ private void updateWifiStateListeners(boolean shouldListen) {
+ mWifiStateReceiver.setListening(shouldListen);
+ if (shouldListen) {
+ mWifiManager.registerSoftApCallback(
+ this,
+ Dependency.get(Dependency.MAIN_HANDLER));
+ } else {
+ mWifiManager.unregisterSoftApCallback(this);
}
}
@@ -116,20 +140,55 @@
if (DEBUG) Log.d(TAG, "Starting tethering");
mConnectivityManager.startTethering(
ConnectivityManager.TETHERING_WIFI, false, callback);
- fireCallback(isHotspotEnabled());
+ fireHotspotChangedCallback(isHotspotEnabled());
} else {
mConnectivityManager.stopTethering(ConnectivityManager.TETHERING_WIFI);
}
}
- private void fireCallback(boolean isEnabled) {
+ @Override
+ public int getNumConnectedDevices() {
+ return mNumConnectedDevices;
+ }
+
+ /**
+ * Sends a hotspot changed callback with the new enabled status. Wraps
+ * {@link #fireHotspotChangedCallback(boolean, int)} and assumes that the number of devices has
+ * not changed.
+ *
+ * @param enabled whether the hotspot is enabled
+ */
+ private void fireHotspotChangedCallback(boolean enabled) {
+ fireHotspotChangedCallback(enabled, mNumConnectedDevices);
+ }
+
+ /**
+ * Sends a hotspot changed callback with the new enabled status & the number of devices
+ * connected to the hotspot. Be careful when calling over multiple threads, especially if one of
+ * them is the main thread (as it can be blocked).
+ *
+ * @param enabled whether the hotspot is enabled
+ * @param numConnectedDevices number of devices connected to the hotspot
+ */
+ private void fireHotspotChangedCallback(boolean enabled, int numConnectedDevices) {
synchronized (mCallbacks) {
for (Callback callback : mCallbacks) {
- callback.onHotspotChanged(isEnabled);
+ callback.onHotspotChanged(enabled, numConnectedDevices);
}
}
}
+ @Override
+ public void onStateChanged(int state, int failureReason) {
+ // Do nothing - we don't care about changing anything here.
+ }
+
+ @Override
+ public void onNumClientsChanged(int numConnectedDevices) {
+ mNumConnectedDevices = numConnectedDevices;
+ fireHotspotChangedCallback(isHotspotEnabled(), numConnectedDevices);
+ }
+
private final class OnStartTetheringCallback extends
ConnectivityManager.OnStartTetheringCallback {
@Override
@@ -143,12 +202,15 @@
public void onTetheringFailed() {
if (DEBUG) Log.d(TAG, "onTetheringFailed");
mWaitingForCallback = false;
- fireCallback(isHotspotEnabled());
+ fireHotspotChangedCallback(isHotspotEnabled());
// TODO: Show error.
}
}
- private final class Receiver extends BroadcastReceiver {
+ /**
+ * Class to listen in on wifi state and update the hotspot state
+ */
+ private final class WifiStateReceiver extends BroadcastReceiver {
private boolean mRegistered;
public void setListening(boolean listening) {
@@ -170,8 +232,17 @@
int state = intent.getIntExtra(
WifiManager.EXTRA_WIFI_AP_STATE, WifiManager.WIFI_AP_STATE_FAILED);
if (DEBUG) Log.d(TAG, "onReceive " + state);
+
+ // Update internal hotspot state for tracking before using any enabled/callback methods.
mHotspotState = state;
- fireCallback(mHotspotState == WifiManager.WIFI_AP_STATE_ENABLED);
+
+ if (!isHotspotEnabled()) {
+ // Reset num devices if the hotspot is no longer enabled so we don't get ghost
+ // counters.
+ mNumConnectedDevices = 0;
+ }
+
+ fireHotspotChangedCallback(isHotspotEnabled());
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index e76bf57..385438c 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -368,16 +368,16 @@
mController.setActiveStream(row.stream);
if (row.stream == AudioManager.STREAM_RING) {
final boolean hasVibrator = mController.hasVibrator();
- if (mState.ringerModeExternal == AudioManager.RINGER_MODE_NORMAL) {
+ if (mState.ringerModeInternal == AudioManager.RINGER_MODE_NORMAL) {
if (hasVibrator) {
- mController.setRingerMode(AudioManager.RINGER_MODE_VIBRATE, true);
+ mController.setRingerMode(AudioManager.RINGER_MODE_VIBRATE, false);
} else {
final boolean wasZero = row.ss.level == 0;
mController.setStreamVolume(stream,
wasZero ? row.lastAudibleLevel : 0);
}
} else {
- mController.setRingerMode(AudioManager.RINGER_MODE_NORMAL, true);
+ mController.setRingerMode(AudioManager.RINGER_MODE_NORMAL, false);
if (row.ss.level == 0) {
mController.setStreamVolume(stream, 1);
}
@@ -403,15 +403,15 @@
return;
}
final boolean hasVibrator = mController.hasVibrator();
- if (mState.ringerModeExternal == AudioManager.RINGER_MODE_NORMAL) {
+ if (mState.ringerModeInternal == AudioManager.RINGER_MODE_NORMAL) {
if (hasVibrator) {
- mController.setRingerMode(AudioManager.RINGER_MODE_VIBRATE, true);
+ mController.setRingerMode(AudioManager.RINGER_MODE_VIBRATE, false);
} else {
final boolean wasZero = ss.level == 0;
mController.setStreamVolume(AudioManager.STREAM_RING, wasZero ? 1 : 0);
}
} else {
- mController.setRingerMode(AudioManager.RINGER_MODE_NORMAL, true);
+ mController.setRingerMode(AudioManager.RINGER_MODE_NORMAL, false);
if (ss.level == 0) {
mController.setStreamVolume(AudioManager.STREAM_RING, 1);
}
@@ -552,7 +552,7 @@
if (ss == null) {
return;
}
- switch (mState.ringerModeExternal) {
+ switch (mState.ringerModeInternal) {
case AudioManager.RINGER_MODE_VIBRATE:
mRingerStatus.setText(R.string.volume_ringer_status_vibrate);
mRingerIcon.setImageResource(R.drawable.ic_volume_ringer_vibrate);
@@ -653,9 +653,9 @@
final boolean isAlarmStream = row.stream == AudioManager.STREAM_ALARM;
final boolean isMusicStream = row.stream == AudioManager.STREAM_MUSIC;
final boolean isRingVibrate = isRingStream
- && mState.ringerModeExternal == AudioManager.RINGER_MODE_VIBRATE;
+ && mState.ringerModeInternal == AudioManager.RINGER_MODE_VIBRATE;
final boolean isRingSilent = isRingStream
- && mState.ringerModeExternal == AudioManager.RINGER_MODE_SILENT;
+ && mState.ringerModeInternal == AudioManager.RINGER_MODE_SILENT;
final boolean isZenAlarms = mState.zenMode == Global.ZEN_MODE_ALARMS;
final boolean isZenNone = mState.zenMode == Global.ZEN_MODE_NO_INTERRUPTIONS;
final boolean zenMuted = isZenAlarms ? (isRingStream || isSystemStream)
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml
index 859dc2f..f5e079c 100644
--- a/packages/SystemUI/tests/AndroidManifest.xml
+++ b/packages/SystemUI/tests/AndroidManifest.xml
@@ -47,6 +47,7 @@
<uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" />
<uses-permission android:name="android.permission.REAL_GET_TASKS" />
<uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW" />
+ <uses-permission android:name="android.permission.NETWORK_SETTINGS" />
<application>
<uses-library android:name="android.test.runner" />
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeHotspotController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeHotspotController.java
index 5491147..016160a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeHotspotController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeHotspotController.java
@@ -44,4 +44,9 @@
public boolean isHotspotSupported() {
return false;
}
+
+ @Override
+ public int getNumConnectedDevices() {
+ return 0;
+ }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
index 0a03b7f..0bc95f4 100644
--- a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
@@ -37,7 +37,9 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Handler;
+import android.os.Looper;
import android.os.Message;
+import android.util.Log;
import android.util.MathUtils;
import android.util.Slog;
import android.util.TypedValue;
@@ -154,6 +156,12 @@
MagnificationController magnificationController,
boolean detectTripleTap,
boolean detectShortcutTrigger) {
+ if (DEBUG_ALL) {
+ Log.i(LOG_TAG,
+ "MagnificationGestureHandler(detectTripleTap = " + detectTripleTap
+ + ", detectShortcutTrigger = " + detectShortcutTrigger + ")");
+ }
+
mMagnificationController = magnificationController;
mDelegatingState = new DelegatingState();
@@ -581,7 +589,7 @@
@VisibleForTesting boolean mShortcutTriggered;
- Handler mHandler = new Handler(this);
+ @VisibleForTesting Handler mHandler = new Handler(this);
public DetectingState(Context context) {
mLongTapMinDelay = ViewConfiguration.getLongPressTimeout();
@@ -756,11 +764,14 @@
@Override
public void clear() {
setShortcutTriggered(false);
- mHandler.removeMessages(MESSAGE_ON_TRIPLE_TAP_AND_HOLD);
- mHandler.removeMessages(MESSAGE_TRANSITION_TO_DELEGATING_STATE);
+ removePendingDelayedMessages();
clearDelayedMotionEvents();
}
+ private void removePendingDelayedMessages() {
+ mHandler.removeMessages(MESSAGE_ON_TRIPLE_TAP_AND_HOLD);
+ mHandler.removeMessages(MESSAGE_TRANSITION_TO_DELEGATING_STATE);
+ }
private void cacheDelayedMotionEvent(MotionEvent event, MotionEvent rawEvent,
int policyFlags) {
@@ -811,7 +822,7 @@
void transitionToDelegatingStateAndClear() {
transitionTo(mDelegatingState);
sendDelayedMotionEvents();
- clear();
+ removePendingDelayedMessages();
}
private void onTripleTap(MotionEvent up) {
@@ -860,6 +871,7 @@
if (mShortcutTriggered == state) {
return;
}
+ if (DEBUG_DETECTING) Slog.i(LOG_TAG, "setShortcutTriggered(" + state + ")");
mShortcutTriggered = state;
mMagnificationController.setForceShowMagnifiableBounds(state);
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 978ed25..0e2ca14 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -119,6 +119,7 @@
private final LocalLog mRequestsHistory = new LocalLog(20);
private final LocalLog mUiLatencyHistory = new LocalLog(20);
+ private final LocalLog mWtfHistory = new LocalLog(50);
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
@@ -310,7 +311,8 @@
AutofillManagerServiceImpl service = mServicesCache.get(resolvedUserId);
if (service == null) {
service = new AutofillManagerServiceImpl(mContext, mLock, mRequestsHistory,
- mUiLatencyHistory, resolvedUserId, mUi, mDisabledUsers.get(resolvedUserId));
+ mUiLatencyHistory, mWtfHistory, resolvedUserId, mUi,
+ mDisabledUsers.get(resolvedUserId));
mServicesCache.put(userId, service);
}
return service;
@@ -878,10 +880,12 @@
mUi.dump(pw);
}
if (showHistory) {
- pw.println("Requests history:");
+ pw.println(); pw.println("Requests history:"); pw.println();
mRequestsHistory.reverseDump(fd, pw, args);
- pw.println("UI latency history:");
+ pw.println(); pw.println("UI latency history:"); pw.println();
mUiLatencyHistory.reverseDump(fd, pw, args);
+ pw.println(); pw.println("WTF history:"); pw.println();
+ mWtfHistory.reverseDump(fd, pw, args);
}
} finally {
setDebugLocked(oldDebug);
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 6bcfc4b..07b0b77 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -43,7 +43,6 @@
import android.os.Bundle;
import android.os.IBinder;
import android.os.Looper;
-import android.os.RemoteCallback;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -116,6 +115,7 @@
private final LocalLog mRequestsHistory;
private final LocalLog mUiLatencyHistory;
+ private final LocalLog mWtfHistory;
private final FieldClassificationStrategy mFieldClassificationStrategy;
/**
@@ -179,11 +179,13 @@
private long mLastPrune = 0;
AutofillManagerServiceImpl(Context context, Object lock, LocalLog requestsHistory,
- LocalLog uiLatencyHistory, int userId, AutoFillUI ui, boolean disabled) {
+ LocalLog uiLatencyHistory, LocalLog wtfHistory, int userId, AutoFillUI ui,
+ boolean disabled) {
mContext = context;
mLock = lock;
mRequestsHistory = requestsHistory;
mUiLatencyHistory = uiLatencyHistory;
+ mWtfHistory = wtfHistory;
mUserId = userId;
mUi = ui;
mFieldClassificationStrategy = new FieldClassificationStrategy(context, userId);
@@ -484,8 +486,8 @@
assertCallerLocked(componentName);
final Session newSession = new Session(this, mUi, mContext, mHandlerCaller, mUserId, mLock,
- sessionId, uid, activityToken, appCallbackToken, hasCallback,
- mUiLatencyHistory, mInfo.getServiceInfo().getComponentName(), componentName, flags);
+ sessionId, uid, activityToken, appCallbackToken, hasCallback, mUiLatencyHistory,
+ mWtfHistory, mInfo.getServiceInfo().getComponentName(), componentName, flags);
mSessions.put(newSession.id, newSession);
return newSession;
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 63f8384..e1fb3a7 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -210,6 +210,9 @@
@GuardedBy("mLock")
private final LocalLog mUiLatencyHistory;
+ @GuardedBy("mLock")
+ private final LocalLog mWtfHistory;
+
/**
* Receiver of assist data from the app's {@link Activity}.
*/
@@ -241,7 +244,13 @@
// ONE_WAY warning because system_service could block on app calls. We need to
// change AssistStructure so it provides a "one-way" writeToParcel() method that
// sends all the data
- structure.ensureData();
+ try {
+ structure.ensureData();
+ } catch (RuntimeException e) {
+ wtf(e, "Exception lazy loading assist structure for %s: %s",
+ structure.getActivityComponent(), e);
+ return;
+ }
// Sanitize structure before it's sent to service.
final ComponentName componentNameFromApp = structure.getActivityComponent();
@@ -447,6 +456,7 @@
@NonNull Context context, @NonNull HandlerCaller handlerCaller, int userId,
@NonNull Object lock, int sessionId, int uid, @NonNull IBinder activityToken,
@NonNull IBinder client, boolean hasCallback, @NonNull LocalLog uiLatencyHistory,
+ @NonNull LocalLog wtfHistory,
@NonNull ComponentName serviceComponentName, @NonNull ComponentName componentName,
int flags) {
id = sessionId;
@@ -461,6 +471,7 @@
mActivityToken = activityToken;
mHasCallback = hasCallback;
mUiLatencyHistory = uiLatencyHistory;
+ mWtfHistory = wtfHistory;
mComponentName = componentName;
mClient = IAutoFillManagerClient.Stub.asInterface(client);
@@ -1102,8 +1113,7 @@
if (userData != null && fcStrategy != null) {
logFieldClassificationScoreLocked(fcStrategy, ignoredDatasets, changedFieldIds,
changedDatasetIds, manuallyFilledFieldIds, manuallyFilledDatasetIds,
- manuallyFilledIds, userData,
- mViewStates.values());
+ userData, mViewStates.values());
} else {
mService.logContextCommittedLocked(id, mClientState, mSelectedDatasetIds,
ignoredDatasets, changedFieldIds, changedDatasetIds,
@@ -1123,7 +1133,6 @@
@NonNull ArrayList<String> changedDatasetIds,
@NonNull ArrayList<AutofillId> manuallyFilledFieldIds,
@NonNull ArrayList<ArrayList<String>> manuallyFilledDatasetIds,
- @NonNull ArrayMap<AutofillId, ArraySet<String>> manuallyFilledIds,
@NonNull UserData userData, @NonNull Collection<ViewState> viewStates) {
final String[] userValues = userData.getValues();
@@ -1201,8 +1210,7 @@
}
}
} catch (ArrayIndexOutOfBoundsException e) {
- Slog.wtf(TAG, "Error accessing FC score at " + i + " x " + j + ": "
- + Arrays.toString(scores.scores), e);
+ wtf(e, "Error accessing FC score at [%d, %d] (%s): %s", i, j, scores, e);
return;
}
@@ -2151,9 +2159,10 @@
final Intent fillInIntent = new Intent();
final FillContext context = getFillContextByRequestIdLocked(requestId);
+
if (context == null) {
- Slog.wtf(TAG, "createAuthFillInIntentLocked(): no FillContext. requestId=" + requestId
- + "; mContexts= " + mContexts);
+ wtf(null, "createAuthFillInIntentLocked(): no FillContext. requestId=%d; mContexts=%s",
+ requestId, mContexts);
return null;
}
fillInIntent.putExtra(AutofillManager.EXTRA_ASSIST_STRUCTURE, context.getStructure());
@@ -2418,4 +2427,15 @@
private void writeLog(int category) {
mMetricsLogger.write(newLogMaker(category));
}
+
+ private void wtf(@Nullable Exception e, String fmt, Object...args) {
+ final String message = String.format(fmt, args);
+ mWtfHistory.log(message);
+
+ if (e != null) {
+ Slog.wtf(TAG, message, e);
+ } else {
+ Slog.wtf(TAG, message);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/ForceAppStandbyTracker.java b/services/core/java/com/android/server/ForceAppStandbyTracker.java
index de113a6..792fdfe 100644
--- a/services/core/java/com/android/server/ForceAppStandbyTracker.java
+++ b/services/core/java/com/android/server/ForceAppStandbyTracker.java
@@ -17,6 +17,7 @@
import android.annotation.NonNull;
import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
import android.app.AppOpsManager;
import android.app.AppOpsManager.PackageOps;
import android.app.IActivityManager;
@@ -504,7 +505,7 @@
*/
void uidToForeground(int uid) {
synchronized (mLock) {
- if (!UserHandle.isApp(uid)) {
+ if (UserHandle.isCore(uid)) {
return;
}
// TODO This can be optimized by calling indexOfKey and sharing the index for get and
@@ -522,7 +523,7 @@
*/
void uidToBackground(int uid, boolean remove) {
synchronized (mLock) {
- if (!UserHandle.isApp(uid)) {
+ if (UserHandle.isCore(uid)) {
return;
}
// TODO This can be optimized by calling indexOfKey and sharing the index for get and
@@ -825,9 +826,10 @@
/**
* @return whether jobs should be restricted for a UID package-name.
*/
- public boolean areJobsRestricted(int uid, @NonNull String packageName) {
+ public boolean areJobsRestricted(int uid, @NonNull String packageName,
+ boolean hasForegroundExemption) {
return isRestricted(uid, packageName, /*useTempWhitelistToo=*/ true,
- /* exemptOnBatterySaver =*/ false);
+ hasForegroundExemption);
}
/**
@@ -861,10 +863,12 @@
/**
* @return whether a UID is in the foreground or not.
*
- * Note clients normally shouldn't need to access it. It's only for dumpsys.
+ * Note this information is based on the UID proc state callback, meaning it's updated
+ * asynchronously and may subtly be stale. If the fresh data is needed, use
+ * {@link ActivityManagerInternal#getUidProcessState} instead.
*/
public boolean isInForeground(int uid) {
- if (!UserHandle.isApp(uid)) {
+ if (UserHandle.isCore(uid)) {
return true;
}
synchronized (mLock) {
diff --git a/services/core/java/com/android/server/am/ActiveInstrumentation.java b/services/core/java/com/android/server/am/ActiveInstrumentation.java
index 84e4ea9..4a65733 100644
--- a/services/core/java/com/android/server/am/ActiveInstrumentation.java
+++ b/services/core/java/com/android/server/am/ActiveInstrumentation.java
@@ -22,6 +22,9 @@
import android.content.pm.ApplicationInfo;
import android.os.Bundle;
import android.util.PrintWriterPrinter;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.server.am.proto.ActiveInstrumentationProto;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -119,4 +122,26 @@
pw.print(prefix); pw.print("mArguments=");
pw.println(mArguments);
}
+
+ void writeToProto(ProtoOutputStream proto, long fieldId) {
+ long token = proto.start(fieldId);
+ mClass.writeToProto(proto, ActiveInstrumentationProto.CLASS);
+ proto.write(ActiveInstrumentationProto.FINISHED, mFinished);
+ for (int i=0; i<mRunningProcesses.size(); i++) {
+ mRunningProcesses.get(i).writeToProto(proto,
+ ActiveInstrumentationProto.RUNNING_PROCESSES);
+ }
+ for (String p : mTargetProcesses) {
+ proto.write(ActiveInstrumentationProto.TARGET_PROCESSES, p);
+ }
+ if (mTargetInfo != null) {
+ mTargetInfo.writeToProto(proto, ActiveInstrumentationProto.TARGET_INFO);
+ }
+ proto.write(ActiveInstrumentationProto.PROFILE_FILE, mProfileFile);
+ proto.write(ActiveInstrumentationProto.WATCHER, mWatcher.toString());
+ proto.write(ActiveInstrumentationProto.UI_AUTOMATION_CONNECTION,
+ mUiAutomationConnection.toString());
+ proto.write(ActiveInstrumentationProto.ARGUMENTS, mArguments.toString());
+ proto.end(token);
+ }
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 1eec982..5eb5b14 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -216,6 +216,7 @@
import android.app.ActivityManagerInternal;
import android.app.ActivityManagerInternal.ScreenObserver;
import android.app.ActivityManagerInternal.SleepToken;
+import android.app.ActivityManagerProto;
import android.app.ActivityOptions;
import android.app.ActivityThread;
import android.app.AlertDialog;
@@ -372,6 +373,7 @@
import android.util.TimingsTraceLog;
import android.util.Xml;
import android.util.proto.ProtoOutputStream;
+import android.util.proto.ProtoUtils;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.RemoteAnimationDefinition;
@@ -428,12 +430,16 @@
import com.android.server.ThreadPriorityBooster;
import com.android.server.Watchdog;
import com.android.server.am.ActivityStack.ActivityState;
-import com.android.server.am.EventLogTags;
import com.android.server.am.proto.ActivityManagerServiceProto;
import com.android.server.am.proto.BroadcastProto;
import com.android.server.am.proto.GrantUriProto;
+import com.android.server.am.proto.ImportanceTokenProto;
import com.android.server.am.proto.MemInfoProto;
import com.android.server.am.proto.NeededUriGrantsProto;
+import com.android.server.am.proto.ProcessOomProto;
+import com.android.server.am.proto.ProcessToGcProto;
+import com.android.server.am.proto.ProcessesProto;
+import com.android.server.am.proto.ProcessesProto.UidObserverRegistrationProto;
import com.android.server.am.proto.StickyBroadcastProto;
import com.android.server.firewall.IntentFirewall;
import com.android.server.job.JobSchedulerInternal;
@@ -939,6 +945,16 @@
return "ImportanceToken { " + Integer.toHexString(System.identityHashCode(this))
+ " " + reason + " " + pid + " " + token + " }";
}
+
+ void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long pToken = proto.start(fieldId);
+ proto.write(ImportanceTokenProto.PID, pid);
+ if (token != null) {
+ proto.write(ImportanceTokenProto.TOKEN, token.toString());
+ }
+ proto.write(ImportanceTokenProto.REASON, reason);
+ proto.end(pToken);
+ }
}
final SparseArray<ImportanceToken> mImportantProcesses = new SparseArray<ImportanceToken>();
@@ -1317,6 +1333,14 @@
duration = _duration;
tag = _tag;
}
+
+ void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(ProcessesProto.PendingTempWhitelist.TARGET_UID, targetUid);
+ proto.write(ProcessesProto.PendingTempWhitelist.DURATION_MS, duration);
+ proto.write(ProcessesProto.PendingTempWhitelist.TAG, tag);
+ proto.end(token);
+ }
}
final SparseArray<PendingTempWhitelist> mPendingTempWhitelist = new SparseArray<>();
@@ -1641,6 +1665,20 @@
final SparseIntArray lastProcStates;
+ // Please keep the enum lists in sync
+ private static int[] ORIG_ENUMS = new int[]{
+ ActivityManager.UID_OBSERVER_IDLE,
+ ActivityManager.UID_OBSERVER_ACTIVE,
+ ActivityManager.UID_OBSERVER_GONE,
+ ActivityManager.UID_OBSERVER_PROCSTATE,
+ };
+ private static int[] PROTO_ENUMS = new int[]{
+ ActivityManagerProto.UID_OBSERVER_FLAG_IDLE,
+ ActivityManagerProto.UID_OBSERVER_FLAG_ACTIVE,
+ ActivityManagerProto.UID_OBSERVER_FLAG_GONE,
+ ActivityManagerProto.UID_OBSERVER_FLAG_PROCSTATE,
+ };
+
UidObserverRegistration(int _uid, String _pkg, int _which, int _cutpoint) {
uid = _uid;
pkg = _pkg;
@@ -1652,6 +1690,25 @@
lastProcStates = null;
}
}
+
+ void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(UidObserverRegistrationProto.UID, uid);
+ proto.write(UidObserverRegistrationProto.PACKAGE, pkg);
+ ProtoUtils.writeBitWiseFlagsToProtoEnum(proto, UidObserverRegistrationProto.FLAGS,
+ which, ORIG_ENUMS, PROTO_ENUMS);
+ proto.write(UidObserverRegistrationProto.CUT_POINT, cutpoint);
+ if (lastProcStates != null) {
+ final int NI = lastProcStates.size();
+ for (int i=0; i<NI; i++) {
+ final long pToken = proto.start(UidObserverRegistrationProto.LAST_PROC_STATES);
+ proto.write(UidObserverRegistrationProto.ProcState.UID, lastProcStates.keyAt(i));
+ proto.write(UidObserverRegistrationProto.ProcState.STATE, lastProcStates.valueAt(i));
+ proto.end(pToken);
+ }
+ }
+ proto.end(token);
+ }
}
final List<ScreenObserver> mScreenObservers = new ArrayList<>();
@@ -8849,7 +8906,7 @@
case AppOpsManager.MODE_ALLOWED:
// If force-background-check is enabled, restrict all apps that aren't whitelisted.
if (mForceBackgroundCheck &&
- UserHandle.isApp(uid) &&
+ !UserHandle.isCore(uid) &&
!isOnDeviceIdleWhitelistLocked(uid)) {
if (DEBUG_BACKGROUND_CHECK) {
Slog.i(TAG, "Force background check: " +
@@ -15251,7 +15308,6 @@
boolean dumpVisibleStacksOnly = false;
boolean dumpFocusedStackOnly = false;
String dumpPackage = null;
- int dumpAppId = -1;
int opti = 0;
while (opti < args.length) {
@@ -15325,6 +15381,15 @@
}
} else if ("service".equals(cmd)) {
mServices.writeToProto(proto);
+ } else if ("processes".equals(cmd) || "p".equals(cmd)) {
+ if (opti < args.length) {
+ dumpPackage = args[opti];
+ opti++;
+ }
+ // output proto is ProcessProto
+ synchronized (this) {
+ writeProcessesToProtoLocked(proto, dumpPackage);
+ }
} else {
// default option, dump everything, output is ActivityManagerServiceProto
synchronized (this) {
@@ -15339,6 +15404,10 @@
long serviceToken = proto.start(ActivityManagerServiceProto.SERVICES);
mServices.writeToProto(proto);
proto.end(serviceToken);
+
+ long processToken = proto.start(ActivityManagerServiceProto.PROCESSES);
+ writeProcessesToProtoLocked(proto, dumpPackage);
+ proto.end(processToken);
}
}
proto.flush();
@@ -15346,16 +15415,7 @@
return;
}
- if (dumpPackage != null) {
- try {
- ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(
- dumpPackage, 0);
- dumpAppId = UserHandle.getAppId(info.uid);
- } catch (NameNotFoundException e) {
- e.printStackTrace();
- }
- }
-
+ int dumpAppId = getAppId(dumpPackage);
boolean more = false;
// Is the caller requesting to dump a particular piece of data?
if (opti < args.length) {
@@ -15397,33 +15457,17 @@
pw.println(BinderInternal.nGetBinderProxyCount(Integer.parseInt(uid)));
}
} else if ("broadcasts".equals(cmd) || "b".equals(cmd)) {
- String[] newArgs;
- String name;
- if (opti >= args.length) {
- name = null;
- newArgs = EMPTY_STRING_ARRAY;
- } else {
+ if (opti < args.length) {
dumpPackage = args[opti];
opti++;
- newArgs = new String[args.length - opti];
- if (args.length > 2) System.arraycopy(args, opti, newArgs, 0,
- args.length - opti);
}
synchronized (this) {
dumpBroadcastsLocked(fd, pw, args, opti, true, dumpPackage);
}
} else if ("broadcast-stats".equals(cmd)) {
- String[] newArgs;
- String name;
- if (opti >= args.length) {
- name = null;
- newArgs = EMPTY_STRING_ARRAY;
- } else {
+ if (opti < args.length) {
dumpPackage = args[opti];
opti++;
- newArgs = new String[args.length - opti];
- if (args.length > 2) System.arraycopy(args, opti, newArgs, 0,
- args.length - opti);
}
synchronized (this) {
if (dumpCheckinFormat) {
@@ -15434,33 +15478,17 @@
}
}
} else if ("intents".equals(cmd) || "i".equals(cmd)) {
- String[] newArgs;
- String name;
- if (opti >= args.length) {
- name = null;
- newArgs = EMPTY_STRING_ARRAY;
- } else {
+ if (opti < args.length) {
dumpPackage = args[opti];
opti++;
- newArgs = new String[args.length - opti];
- if (args.length > 2) System.arraycopy(args, opti, newArgs, 0,
- args.length - opti);
}
synchronized (this) {
dumpPendingIntentsLocked(fd, pw, args, opti, true, dumpPackage);
}
} else if ("processes".equals(cmd) || "p".equals(cmd)) {
- String[] newArgs;
- String name;
- if (opti >= args.length) {
- name = null;
- newArgs = EMPTY_STRING_ARRAY;
- } else {
+ if (opti < args.length) {
dumpPackage = args[opti];
opti++;
- newArgs = new String[args.length - opti];
- if (args.length > 2) System.arraycopy(args, opti, newArgs, 0,
- args.length - opti);
}
synchronized (this) {
dumpProcessesLocked(fd, pw, args, opti, true, dumpPackage, dumpAppId);
@@ -15881,8 +15909,21 @@
}
}
+ private int getAppId(String dumpPackage) {
+ if (dumpPackage != null) {
+ try {
+ ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(
+ dumpPackage, 0);
+ return UserHandle.getAppId(info.uid);
+ } catch (NameNotFoundException e) {
+ e.printStackTrace();
+ }
+ }
+ return -1;
+ }
+
boolean dumpUids(PrintWriter pw, String dumpPackage, int dumpAppId, SparseArray<UidRecord> uids,
- String header, boolean needSep) {
+ String header, boolean needSep) {
boolean printed = false;
for (int i=0; i<uids.size(); i++) {
UidRecord uidRec = uids.valueAt(i);
@@ -16100,7 +16141,7 @@
"OnHold Norm", "OnHold PERS", dumpPackage);
}
- needSep = dumpProcessesToGc(fd, pw, args, opti, needSep, dumpAll, dumpPackage);
+ needSep = dumpProcessesToGc(pw, needSep, dumpPackage);
needSep = mAppErrors.dumpLocked(fd, pw, needSep, dumpPackage);
@@ -16385,8 +16426,327 @@
pw.println(" mForceBackgroundCheck=" + mForceBackgroundCheck);
}
- boolean dumpProcessesToGc(FileDescriptor fd, PrintWriter pw, String[] args,
- int opti, boolean needSep, boolean dumpAll, String dumpPackage) {
+ void writeProcessesToProtoLocked(ProtoOutputStream proto, String dumpPackage) {
+ int numPers = 0;
+
+ final int NP = mProcessNames.getMap().size();
+ for (int ip=0; ip<NP; ip++) {
+ SparseArray<ProcessRecord> procs = mProcessNames.getMap().valueAt(ip);
+ final int NA = procs.size();
+ for (int ia = 0; ia<NA; ia++) {
+ ProcessRecord r = procs.valueAt(ia);
+ if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) {
+ continue;
+ }
+ r.writeToProto(proto, ProcessesProto.PROCS);
+ if (r.persistent) {
+ numPers++;
+ }
+ }
+ }
+
+ for (int i=0; i<mIsolatedProcesses.size(); i++) {
+ ProcessRecord r = mIsolatedProcesses.valueAt(i);
+ if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) {
+ continue;
+ }
+ r.writeToProto(proto, ProcessesProto.ISOLATED_PROCS);
+ }
+
+ for (int i=0; i<mActiveInstrumentation.size(); i++) {
+ ActiveInstrumentation ai = mActiveInstrumentation.get(i);
+ if (dumpPackage != null && !ai.mClass.getPackageName().equals(dumpPackage)
+ && !ai.mTargetInfo.packageName.equals(dumpPackage)) {
+ continue;
+ }
+ ai.writeToProto(proto, ProcessesProto.ACTIVE_INSTRUMENTATIONS);
+ }
+
+ int whichAppId = getAppId(dumpPackage);
+ for (int i=0; i<mActiveUids.size(); i++) {
+ UidRecord uidRec = mActiveUids.valueAt(i);
+ if (dumpPackage != null && UserHandle.getAppId(uidRec.uid) != whichAppId) {
+ continue;
+ }
+ uidRec.writeToProto(proto, ProcessesProto.ACTIVE_UIDS);
+ }
+
+ for (int i=0; i<mValidateUids.size(); i++) {
+ UidRecord uidRec = mValidateUids.valueAt(i);
+ if (dumpPackage != null && UserHandle.getAppId(uidRec.uid) != whichAppId) {
+ continue;
+ }
+ uidRec.writeToProto(proto, ProcessesProto.VALIDATE_UIDS);
+ }
+
+ if (mLruProcesses.size() > 0) {
+ long lruToken = proto.start(ProcessesProto.LRU_PROCS);
+ int total = mLruProcesses.size();
+ proto.write(ProcessesProto.LruProcesses.SIZE, total);
+ proto.write(ProcessesProto.LruProcesses.NON_ACT_AT, total-mLruProcessActivityStart);
+ proto.write(ProcessesProto.LruProcesses.NON_SVC_AT, total-mLruProcessServiceStart);
+ writeProcessOomListToProto(proto, ProcessesProto.LruProcesses.LIST, this,
+ mLruProcesses,false, dumpPackage);
+ proto.end(lruToken);
+ }
+
+ if (dumpPackage != null) {
+ synchronized (mPidsSelfLocked) {
+ for (int i=0; i<mPidsSelfLocked.size(); i++) {
+ ProcessRecord r = mPidsSelfLocked.valueAt(i);
+ if (!r.pkgList.containsKey(dumpPackage)) {
+ continue;
+ }
+ r.writeToProto(proto, ProcessesProto.PIDS_SELF_LOCKED);
+ }
+ }
+ }
+
+ if (mImportantProcesses.size() > 0) {
+ synchronized (mPidsSelfLocked) {
+ for (int i=0; i<mImportantProcesses.size(); i++) {
+ ImportanceToken it = mImportantProcesses.valueAt(i);
+ ProcessRecord r = mPidsSelfLocked.get(it.pid);
+ if (dumpPackage != null && (r == null
+ || !r.pkgList.containsKey(dumpPackage))) {
+ continue;
+ }
+ it.writeToProto(proto, ProcessesProto.IMPORTANT_PROCS);
+ }
+ }
+ }
+
+ for (int i=0; i<mPersistentStartingProcesses.size(); i++) {
+ ProcessRecord r = mPersistentStartingProcesses.get(i);
+ if (dumpPackage != null && !dumpPackage.equals(r.info.packageName)) {
+ continue;
+ }
+ r.writeToProto(proto, ProcessesProto.PERSISTENT_STARTING_PROCS);
+ }
+
+ for (int i=0; i<mRemovedProcesses.size(); i++) {
+ ProcessRecord r = mRemovedProcesses.get(i);
+ if (dumpPackage != null && !dumpPackage.equals(r.info.packageName)) {
+ continue;
+ }
+ r.writeToProto(proto, ProcessesProto.REMOVED_PROCS);
+ }
+
+ for (int i=0; i<mProcessesOnHold.size(); i++) {
+ ProcessRecord r = mProcessesOnHold.get(i);
+ if (dumpPackage != null && !dumpPackage.equals(r.info.packageName)) {
+ continue;
+ }
+ r.writeToProto(proto, ProcessesProto.ON_HOLD_PROCS);
+ }
+
+ writeProcessesToGcToProto(proto, ProcessesProto.GC_PROCS, dumpPackage);
+ mAppErrors.writeToProto(proto, ProcessesProto.APP_ERRORS, dumpPackage);
+
+ if (dumpPackage == null) {
+ mUserController.writeToProto(proto, ProcessesProto.USER_CONTROLLER);
+ getGlobalConfiguration().writeToProto(proto, ProcessesProto.GLOBAL_CONFIGURATION);
+ proto.write(ProcessesProto.CONFIG_WILL_CHANGE, getFocusedStack().mConfigWillChange);
+ }
+
+ if (mHomeProcess != null && (dumpPackage == null
+ || mHomeProcess.pkgList.containsKey(dumpPackage))) {
+ mHomeProcess.writeToProto(proto, ProcessesProto.HOME_PROC);
+ }
+
+ if (mPreviousProcess != null && (dumpPackage == null
+ || mPreviousProcess.pkgList.containsKey(dumpPackage))) {
+ mPreviousProcess.writeToProto(proto, ProcessesProto.PREVIOUS_PROC);
+ proto.write(ProcessesProto.PREVIOUS_PROC_VISIBLE_TIME_MS, mPreviousProcessVisibleTime);
+ }
+
+ if (mHeavyWeightProcess != null && (dumpPackage == null
+ || mHeavyWeightProcess.pkgList.containsKey(dumpPackage))) {
+ mHeavyWeightProcess.writeToProto(proto, ProcessesProto.HEAVY_WEIGHT_PROC);
+ }
+
+ for (Map.Entry<String, Integer> entry : mCompatModePackages.getPackages().entrySet()) {
+ String pkg = entry.getKey();
+ int mode = entry.getValue();
+ if (dumpPackage == null || dumpPackage.equals(pkg)) {
+ long compatToken = proto.start(ProcessesProto.SCREEN_COMPAT_PACKAGES);
+ proto.write(ProcessesProto.ScreenCompatPackage.PACKAGE, pkg);
+ proto.write(ProcessesProto.ScreenCompatPackage.MODE, mode);
+ proto.end(compatToken);
+ }
+ }
+
+ final int NI = mUidObservers.getRegisteredCallbackCount();
+ for (int i=0; i<NI; i++) {
+ final UidObserverRegistration reg = (UidObserverRegistration)
+ mUidObservers.getRegisteredCallbackCookie(i);
+ if (dumpPackage == null || dumpPackage.equals(reg.pkg)) {
+ reg.writeToProto(proto, ProcessesProto.UID_OBSERVERS);
+ }
+ }
+
+ for (int v : mDeviceIdleWhitelist) {
+ proto.write(ProcessesProto.DEVICE_IDLE_WHITELIST, v);
+ }
+
+ for (int v : mDeviceIdleTempWhitelist) {
+ proto.write(ProcessesProto.DEVICE_IDLE_TEMP_WHITELIST, v);
+ }
+
+ if (mPendingTempWhitelist.size() > 0) {
+ for (int i=0; i < mPendingTempWhitelist.size(); i++) {
+ mPendingTempWhitelist.valueAt(i).writeToProto(proto,
+ ProcessesProto.PENDING_TEMP_WHITELIST);
+ }
+ }
+
+ if (dumpPackage == null) {
+ final long sleepToken = proto.start(ProcessesProto.SLEEP_STATUS);
+ proto.write(ProcessesProto.SleepStatus.WAKEFULNESS,
+ PowerManagerInternal.wakefulnessToProtoEnum(mWakefulness));
+ for (SleepToken st : mStackSupervisor.mSleepTokens) {
+ proto.write(ProcessesProto.SleepStatus.SLEEP_TOKENS, st.toString());
+ }
+ proto.write(ProcessesProto.SleepStatus.SLEEPING, mSleeping);
+ proto.write(ProcessesProto.SleepStatus.SHUTTING_DOWN, mShuttingDown);
+ proto.write(ProcessesProto.SleepStatus.TEST_PSS_MODE, mTestPssMode);
+ proto.end(sleepToken);
+
+ if (mRunningVoice != null) {
+ final long vrToken = proto.start(ProcessesProto.RUNNING_VOICE);
+ proto.write(ProcessesProto.VoiceProto.SESSION, mRunningVoice.toString());
+ mVoiceWakeLock.writeToProto(proto, ProcessesProto.VoiceProto.WAKELOCK);
+ proto.end(vrToken);
+ }
+
+ mVrController.writeToProto(proto, ProcessesProto.VR_CONTROLLER);
+ }
+
+ if (mDebugApp != null || mOrigDebugApp != null || mDebugTransient
+ || mOrigWaitForDebugger) {
+ if (dumpPackage == null || dumpPackage.equals(mDebugApp)
+ || dumpPackage.equals(mOrigDebugApp)) {
+ final long debugAppToken = proto.start(ProcessesProto.DEBUG);
+ proto.write(ProcessesProto.DebugApp.DEBUG_APP, mDebugApp);
+ proto.write(ProcessesProto.DebugApp.ORIG_DEBUG_APP, mOrigDebugApp);
+ proto.write(ProcessesProto.DebugApp.DEBUG_TRANSIENT, mDebugTransient);
+ proto.write(ProcessesProto.DebugApp.ORIG_WAIT_FOR_DEBUGGER, mOrigWaitForDebugger);
+ proto.end(debugAppToken);
+ }
+ }
+
+ if (mCurAppTimeTracker != null) {
+ mCurAppTimeTracker.writeToProto(proto, ProcessesProto.CURRENT_TRACKER, true);
+ }
+
+ if (mMemWatchProcesses.getMap().size() > 0) {
+ final long token = proto.start(ProcessesProto.MEM_WATCH_PROCESSES);
+ ArrayMap<String, SparseArray<Pair<Long, String>>> procs = mMemWatchProcesses.getMap();
+ for (int i=0; i<procs.size(); i++) {
+ final String proc = procs.keyAt(i);
+ final SparseArray<Pair<Long, String>> uids = procs.valueAt(i);
+ final long ptoken = proto.start(ProcessesProto.MemWatchProcess.PROCS);
+ proto.write(ProcessesProto.MemWatchProcess.Process.NAME, proc);
+ for (int j=0; j<uids.size(); j++) {
+ final long utoken = proto.start(ProcessesProto.MemWatchProcess.Process.MEM_STATS);
+ Pair<Long, String> val = uids.valueAt(j);
+ proto.write(ProcessesProto.MemWatchProcess.Process.MemStats.UID, uids.keyAt(j));
+ proto.write(ProcessesProto.MemWatchProcess.Process.MemStats.SIZE,
+ DebugUtils.sizeValueToString(val.first, new StringBuilder()));
+ proto.write(ProcessesProto.MemWatchProcess.Process.MemStats.REPORT_TO, val.second);
+ proto.end(utoken);
+ }
+ proto.end(ptoken);
+ }
+
+ final long dtoken = proto.start(ProcessesProto.MemWatchProcess.DUMP);
+ proto.write(ProcessesProto.MemWatchProcess.Dump.PROC_NAME, mMemWatchDumpProcName);
+ proto.write(ProcessesProto.MemWatchProcess.Dump.FILE, mMemWatchDumpFile);
+ proto.write(ProcessesProto.MemWatchProcess.Dump.PID, mMemWatchDumpPid);
+ proto.write(ProcessesProto.MemWatchProcess.Dump.UID, mMemWatchDumpUid);
+ proto.end(dtoken);
+
+ proto.end(token);
+ }
+
+ if (mTrackAllocationApp != null) {
+ if (dumpPackage == null || dumpPackage.equals(mTrackAllocationApp)) {
+ proto.write(ProcessesProto.TRACK_ALLOCATION_APP, mTrackAllocationApp);
+ }
+ }
+
+ if (mProfileApp != null || mProfileProc != null || (mProfilerInfo != null &&
+ (mProfilerInfo.profileFile != null || mProfilerInfo.profileFd != null))) {
+ if (dumpPackage == null || dumpPackage.equals(mProfileApp)) {
+ final long token = proto.start(ProcessesProto.PROFILE);
+ proto.write(ProcessesProto.Profile.APP_NAME, mProfileApp);
+ mProfileProc.writeToProto(proto,ProcessesProto.Profile.PROC);
+ if (mProfilerInfo != null) {
+ mProfilerInfo.writeToProto(proto, ProcessesProto.Profile.INFO);
+ proto.write(ProcessesProto.Profile.TYPE, mProfileType);
+ }
+ proto.end(token);
+ }
+ }
+
+ if (dumpPackage == null || dumpPackage.equals(mNativeDebuggingApp)) {
+ proto.write(ProcessesProto.NATIVE_DEBUGGING_APP, mNativeDebuggingApp);
+ }
+
+ if (dumpPackage == null) {
+ proto.write(ProcessesProto.ALWAYS_FINISH_ACTIVITIES, mAlwaysFinishActivities);
+ if (mController != null) {
+ final long token = proto.start(ProcessesProto.CONTROLLER);
+ proto.write(ProcessesProto.Controller.CONTROLLER, mController.toString());
+ proto.write(ProcessesProto.Controller.IS_A_MONKEY, mControllerIsAMonkey);
+ proto.end(token);
+ }
+ proto.write(ProcessesProto.TOTAL_PERSISTENT_PROCS, numPers);
+ proto.write(ProcessesProto.PROCESSES_READY, mProcessesReady);
+ proto.write(ProcessesProto.SYSTEM_READY, mSystemReady);
+ proto.write(ProcessesProto.BOOTED, mBooted);
+ proto.write(ProcessesProto.FACTORY_TEST, mFactoryTest);
+ proto.write(ProcessesProto.BOOTING, mBooting);
+ proto.write(ProcessesProto.CALL_FINISH_BOOTING, mCallFinishBooting);
+ proto.write(ProcessesProto.BOOT_ANIMATION_COMPLETE, mBootAnimationComplete);
+ proto.write(ProcessesProto.LAST_POWER_CHECK_UPTIME_MS, mLastPowerCheckUptime);
+ mStackSupervisor.mGoingToSleep.writeToProto(proto, ProcessesProto.GOING_TO_SLEEP);
+ mStackSupervisor.mLaunchingActivity.writeToProto(proto, ProcessesProto.LAUNCHING_ACTIVITY);
+ proto.write(ProcessesProto.ADJ_SEQ, mAdjSeq);
+ proto.write(ProcessesProto.LRU_SEQ, mLruSeq);
+ proto.write(ProcessesProto.NUM_NON_CACHED_PROCS, mNumNonCachedProcs);
+ proto.write(ProcessesProto.NUM_SERVICE_PROCS, mNumServiceProcs);
+ proto.write(ProcessesProto.NEW_NUM_SERVICE_PROCS, mNewNumServiceProcs);
+ proto.write(ProcessesProto.ALLOW_LOWER_MEM_LEVEL, mAllowLowerMemLevel);
+ proto.write(ProcessesProto.LAST_MEMORY_LEVEL, mLastMemoryLevel);
+ proto.write(ProcessesProto.LAST_NUM_PROCESSES, mLastNumProcesses);
+ long now = SystemClock.uptimeMillis();
+ ProtoUtils.toDuration(proto, ProcessesProto.LAST_IDLE_TIME, mLastIdleTime, now);
+ proto.write(ProcessesProto.LOW_RAM_SINCE_LAST_IDLE_MS, getLowRamTimeSinceIdle(now));
+ }
+
+ }
+
+ void writeProcessesToGcToProto(ProtoOutputStream proto, long fieldId, String dumpPackage) {
+ if (mProcessesToGc.size() > 0) {
+ long now = SystemClock.uptimeMillis();
+ for (int i=0; i<mProcessesToGc.size(); i++) {
+ ProcessRecord r = mProcessesToGc.get(i);
+ if (dumpPackage != null && !dumpPackage.equals(r.info.packageName)) {
+ continue;
+ }
+ final long token = proto.start(fieldId);
+ r.writeToProto(proto, ProcessToGcProto.PROC);
+ proto.write(ProcessToGcProto.REPORT_LOW_MEMORY, r.reportLowMemory);
+ proto.write(ProcessToGcProto.NOW_UPTIME_MS, now);
+ proto.write(ProcessToGcProto.LAST_GCED_MS, r.lastRequestedGc);
+ proto.write(ProcessToGcProto.LAST_LOW_MEMORY_MS, r.lastLowMemory);
+ proto.end(token);
+ }
+ }
+ }
+
+ boolean dumpProcessesToGc(PrintWriter pw, boolean needSep, String dumpPackage) {
if (mProcessesToGc.size() > 0) {
boolean printed = false;
long now = SystemClock.uptimeMillis();
@@ -16464,7 +16824,7 @@
needSep = true;
}
- dumpProcessesToGc(fd, pw, args, opti, needSep, dumpAll, null);
+ dumpProcessesToGc(pw, needSep, null);
pw.println();
pw.println(" mHomeProcess: " + mHomeProcess);
@@ -17015,11 +17375,8 @@
return numPers;
}
- private static final boolean dumpProcessOomList(PrintWriter pw,
- ActivityManagerService service, List<ProcessRecord> origList,
- String prefix, String normalLabel, String persistentLabel,
- boolean inclDetails, String dumpPackage) {
-
+ private static final ArrayList<Pair<ProcessRecord, Integer>>
+ sortProcessOomList(List<ProcessRecord> origList, String dumpPackage) {
ArrayList<Pair<ProcessRecord, Integer>> list
= new ArrayList<Pair<ProcessRecord, Integer>>(origList.size());
for (int i=0; i<origList.size(); i++) {
@@ -17030,10 +17387,6 @@
list.add(new Pair<ProcessRecord, Integer>(origList.get(i), i));
}
- if (list.size() <= 0) {
- return false;
- }
-
Comparator<Pair<ProcessRecord, Integer>> comparator
= new Comparator<Pair<ProcessRecord, Integer>>() {
@Override
@@ -17053,6 +17406,113 @@
};
Collections.sort(list, comparator);
+ return list;
+ }
+
+ private static final boolean writeProcessOomListToProto(ProtoOutputStream proto, long fieldId,
+ ActivityManagerService service, List<ProcessRecord> origList,
+ boolean inclDetails, String dumpPackage) {
+ ArrayList<Pair<ProcessRecord, Integer>> list = sortProcessOomList(origList, dumpPackage);
+ if (list.isEmpty()) return false;
+
+ final long curUptime = SystemClock.uptimeMillis();
+
+ for (int i = list.size() - 1; i >= 0; i--) {
+ ProcessRecord r = list.get(i).first;
+ long token = proto.start(fieldId);
+ String oomAdj = ProcessList.makeOomAdjString(r.setAdj);
+ proto.write(ProcessOomProto.PERSISTENT, r.persistent);
+ proto.write(ProcessOomProto.NUM, (origList.size()-1)-list.get(i).second);
+ proto.write(ProcessOomProto.OOM_ADJ, oomAdj);
+ int schedGroup = ProcessOomProto.SCHED_GROUP_UNKNOWN;
+ switch (r.setSchedGroup) {
+ case ProcessList.SCHED_GROUP_BACKGROUND:
+ schedGroup = ProcessOomProto.SCHED_GROUP_BACKGROUND;
+ break;
+ case ProcessList.SCHED_GROUP_DEFAULT:
+ schedGroup = ProcessOomProto.SCHED_GROUP_DEFAULT;
+ break;
+ case ProcessList.SCHED_GROUP_TOP_APP:
+ schedGroup = ProcessOomProto.SCHED_GROUP_TOP_APP;
+ break;
+ case ProcessList.SCHED_GROUP_TOP_APP_BOUND:
+ schedGroup = ProcessOomProto.SCHED_GROUP_TOP_APP_BOUND;
+ break;
+ }
+ if (schedGroup != ProcessOomProto.SCHED_GROUP_UNKNOWN) {
+ proto.write(ProcessOomProto.SCHED_GROUP, schedGroup);
+ }
+ if (r.foregroundActivities) {
+ proto.write(ProcessOomProto.ACTIVITIES, true);
+ } else if (r.foregroundServices) {
+ proto.write(ProcessOomProto.SERVICES, true);
+ }
+ proto.write(ProcessOomProto.STATE, ProcessList.makeProcStateProtoEnum(r.curProcState));
+ proto.write(ProcessOomProto.TRIM_MEMORY_LEVEL, r.trimMemoryLevel);
+ r.writeToProto(proto, ProcessOomProto.PROC);
+ proto.write(ProcessOomProto.ADJ_TYPE, r.adjType);
+ if (r.adjSource != null || r.adjTarget != null) {
+ if (r.adjTarget instanceof ComponentName) {
+ ComponentName cn = (ComponentName) r.adjTarget;
+ cn.writeToProto(proto, ProcessOomProto.ADJ_TARGET_COMPONENT_NAME);
+ } else if (r.adjTarget != null) {
+ proto.write(ProcessOomProto.ADJ_TARGET_OBJECT, r.adjTarget.toString());
+ }
+ if (r.adjSource instanceof ProcessRecord) {
+ ProcessRecord p = (ProcessRecord) r.adjSource;
+ p.writeToProto(proto, ProcessOomProto.ADJ_SOURCE_PROC);
+ } else if (r.adjSource != null) {
+ proto.write(ProcessOomProto.ADJ_SOURCE_OBJECT, r.adjSource.toString());
+ }
+ }
+ if (inclDetails) {
+ long detailToken = proto.start(ProcessOomProto.DETAIL);
+ proto.write(ProcessOomProto.Detail.MAX_ADJ, r.maxAdj);
+ proto.write(ProcessOomProto.Detail.CUR_RAW_ADJ, r.curRawAdj);
+ proto.write(ProcessOomProto.Detail.SET_RAW_ADJ, r.setRawAdj);
+ proto.write(ProcessOomProto.Detail.CUR_ADJ, r.curAdj);
+ proto.write(ProcessOomProto.Detail.SET_ADJ, r.setAdj);
+ proto.write(ProcessOomProto.Detail.CURRENT_STATE,
+ ProcessList.makeProcStateProtoEnum(r.curProcState));
+ proto.write(ProcessOomProto.Detail.SET_STATE,
+ ProcessList.makeProcStateProtoEnum(r.setProcState));
+ proto.write(ProcessOomProto.Detail.LAST_PSS, DebugUtils.sizeValueToString(
+ r.lastPss*1024, new StringBuilder()));
+ proto.write(ProcessOomProto.Detail.LAST_SWAP_PSS, DebugUtils.sizeValueToString(
+ r.lastSwapPss*1024, new StringBuilder()));
+ proto.write(ProcessOomProto.Detail.LAST_CACHED_PSS, DebugUtils.sizeValueToString(
+ r.lastCachedPss*1024, new StringBuilder()));
+ proto.write(ProcessOomProto.Detail.CACHED, r.cached);
+ proto.write(ProcessOomProto.Detail.EMPTY, r.empty);
+ proto.write(ProcessOomProto.Detail.HAS_ABOVE_CLIENT, r.hasAboveClient);
+
+ if (r.setProcState >= ActivityManager.PROCESS_STATE_SERVICE) {
+ if (r.lastCpuTime != 0) {
+ long uptimeSince = curUptime - service.mLastPowerCheckUptime;
+ long timeUsed = r.curCpuTime - r.lastCpuTime;
+ long cpuTimeToken = proto.start(ProcessOomProto.Detail.SERVICE_RUN_TIME);
+ proto.write(ProcessOomProto.Detail.CpuRunTime.OVER_MS, uptimeSince);
+ proto.write(ProcessOomProto.Detail.CpuRunTime.USED_MS, timeUsed);
+ proto.write(ProcessOomProto.Detail.CpuRunTime.ULTILIZATION,
+ (100.0*timeUsed)/uptimeSince);
+ proto.end(cpuTimeToken);
+ }
+ }
+ proto.end(detailToken);
+ }
+ proto.end(token);
+ }
+
+ return true;
+ }
+
+ private static final boolean dumpProcessOomList(PrintWriter pw,
+ ActivityManagerService service, List<ProcessRecord> origList,
+ String prefix, String normalLabel, String persistentLabel,
+ boolean inclDetails, String dumpPackage) {
+
+ ArrayList<Pair<ProcessRecord, Integer>> list = sortProcessOomList(origList, dumpPackage);
+ if (list.isEmpty()) return false;
final long curUptime = SystemClock.uptimeMillis();
final long uptimeSince = curUptime - service.mLastPowerCheckUptime;
@@ -24120,7 +24580,7 @@
final int size = mActiveUids.size();
for (int i = 0; i < size; i++) {
final int uid = mActiveUids.keyAt(i);
- if (!UserHandle.isApp(uid)) {
+ if (UserHandle.isCore(uid)) {
continue;
}
final UidRecord uidRec = mActiveUids.valueAt(i);
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 0da7e0e..c7d93be 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -22,6 +22,7 @@
import com.android.internal.os.ProcessCpuTracker;
import com.android.server.RescueParty;
import com.android.server.Watchdog;
+import com.android.server.am.proto.AppErrorsProto;
import android.app.ActivityManager;
import android.app.ActivityOptions;
@@ -48,6 +49,7 @@
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
import java.io.File;
import java.io.FileDescriptor;
@@ -103,8 +105,76 @@
mContext = context;
}
- boolean dumpLocked(FileDescriptor fd, PrintWriter pw, boolean needSep,
- String dumpPackage) {
+ void writeToProto(ProtoOutputStream proto, long fieldId, String dumpPackage) {
+ if (mProcessCrashTimes.getMap().isEmpty() && mBadProcesses.getMap().isEmpty()) {
+ return;
+ }
+
+ final long token = proto.start(fieldId);
+ final long now = SystemClock.uptimeMillis();
+ proto.write(AppErrorsProto.NOW_UPTIME_MS, now);
+
+ if (!mProcessCrashTimes.getMap().isEmpty()) {
+ final ArrayMap<String, SparseArray<Long>> pmap = mProcessCrashTimes.getMap();
+ final int procCount = pmap.size();
+ for (int ip = 0; ip < procCount; ip++) {
+ final long ctoken = proto.start(AppErrorsProto.PROCESS_CRASH_TIMES);
+ final String pname = pmap.keyAt(ip);
+ final SparseArray<Long> uids = pmap.valueAt(ip);
+ final int uidCount = uids.size();
+
+ proto.write(AppErrorsProto.ProcessCrashTime.PROCESS_NAME, pname);
+ for (int i = 0; i < uidCount; i++) {
+ final int puid = uids.keyAt(i);
+ final ProcessRecord r = mService.mProcessNames.get(pname, puid);
+ if (dumpPackage != null && (r == null || !r.pkgList.containsKey(dumpPackage))) {
+ continue;
+ }
+ final long etoken = proto.start(AppErrorsProto.ProcessCrashTime.ENTRIES);
+ proto.write(AppErrorsProto.ProcessCrashTime.Entry.UID, puid);
+ proto.write(AppErrorsProto.ProcessCrashTime.Entry.LAST_CRASHED_AT_MS,
+ uids.valueAt(i));
+ proto.end(etoken);
+ }
+ proto.end(ctoken);
+ }
+
+ }
+
+ if (!mBadProcesses.getMap().isEmpty()) {
+ final ArrayMap<String, SparseArray<BadProcessInfo>> pmap = mBadProcesses.getMap();
+ final int processCount = pmap.size();
+ for (int ip = 0; ip < processCount; ip++) {
+ final long btoken = proto.start(AppErrorsProto.BAD_PROCESSES);
+ final String pname = pmap.keyAt(ip);
+ final SparseArray<BadProcessInfo> uids = pmap.valueAt(ip);
+ final int uidCount = uids.size();
+
+ proto.write(AppErrorsProto.BadProcess.PROCESS_NAME, pname);
+ for (int i = 0; i < uidCount; i++) {
+ final int puid = uids.keyAt(i);
+ final ProcessRecord r = mService.mProcessNames.get(pname, puid);
+ if (dumpPackage != null && (r == null
+ || !r.pkgList.containsKey(dumpPackage))) {
+ continue;
+ }
+ final BadProcessInfo info = uids.valueAt(i);
+ final long etoken = proto.start(AppErrorsProto.BadProcess.ENTRIES);
+ proto.write(AppErrorsProto.BadProcess.Entry.UID, puid);
+ proto.write(AppErrorsProto.BadProcess.Entry.CRASHED_AT_MS, info.time);
+ proto.write(AppErrorsProto.BadProcess.Entry.SHORT_MSG, info.shortMsg);
+ proto.write(AppErrorsProto.BadProcess.Entry.LONG_MSG, info.longMsg);
+ proto.write(AppErrorsProto.BadProcess.Entry.STACK, info.stack);
+ proto.end(etoken);
+ }
+ proto.end(btoken);
+ }
+ }
+
+ proto.end(token);
+ }
+
+ boolean dumpLocked(FileDescriptor fd, PrintWriter pw, boolean needSep, String dumpPackage) {
if (!mProcessCrashTimes.getMap().isEmpty()) {
boolean printed = false;
final long now = SystemClock.uptimeMillis();
diff --git a/services/core/java/com/android/server/am/AppTimeTracker.java b/services/core/java/com/android/server/am/AppTimeTracker.java
index 910f33d..d96364a 100644
--- a/services/core/java/com/android/server/am/AppTimeTracker.java
+++ b/services/core/java/com/android/server/am/AppTimeTracker.java
@@ -25,6 +25,10 @@
import android.util.ArrayMap;
import android.util.MutableLong;
import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
+import android.util.proto.ProtoUtils;
+
+import com.android.server.am.proto.AppTimeTrackerProto;
import java.io.PrintWriter;
@@ -119,4 +123,22 @@
pw.print(prefix); pw.print("mStartedPackage="); pw.println(mStartedPackage);
}
}
+
+ void writeToProto(ProtoOutputStream proto, long fieldId, boolean details) {
+ final long token = proto.start(fieldId);
+ proto.write(AppTimeTrackerProto.RECEIVER, mReceiver.toString());
+ proto.write(AppTimeTrackerProto.TOTAL_DURATION_MS, mTotalTime);
+ for (int i=0; i<mPackageTimes.size(); i++) {
+ final long ptoken = proto.start(AppTimeTrackerProto.PACKAGE_TIMES);
+ proto.write(AppTimeTrackerProto.PackageTime.PACKAGE, mPackageTimes.keyAt(i));
+ proto.write(AppTimeTrackerProto.PackageTime.DURATION_MS, mPackageTimes.valueAt(i).value);
+ proto.end(ptoken);
+ }
+ if (details && mStartedTime != 0) {
+ ProtoUtils.toDuration(proto, AppTimeTrackerProto.STARTED_TIME,
+ mStartedTime, SystemClock.elapsedRealtime());
+ proto.write(AppTimeTrackerProto.STARTED_PACKAGE, mStartedPackage);
+ }
+ proto.end(token);
+ }
}
diff --git a/services/core/java/com/android/server/am/ConnectionRecord.java b/services/core/java/com/android/server/am/ConnectionRecord.java
index 6df283c..d320fb1 100644
--- a/services/core/java/com/android/server/am/ConnectionRecord.java
+++ b/services/core/java/com/android/server/am/ConnectionRecord.java
@@ -20,6 +20,7 @@
import android.app.PendingIntent;
import android.content.Context;
import android.util.proto.ProtoOutputStream;
+import android.util.proto.ProtoUtils;
import com.android.server.am.proto.ConnectionRecordProto;
@@ -37,7 +38,43 @@
final PendingIntent clientIntent; // How to launch the client.
String stringName; // Caching of toString.
boolean serviceDead; // Well is it?
-
+
+ // Please keep the following two enum list synced.
+ private static int[] BIND_ORIG_ENUMS = new int[] {
+ Context.BIND_AUTO_CREATE,
+ Context.BIND_DEBUG_UNBIND,
+ Context.BIND_NOT_FOREGROUND,
+ Context.BIND_IMPORTANT_BACKGROUND,
+ Context.BIND_ABOVE_CLIENT,
+ Context.BIND_ALLOW_OOM_MANAGEMENT,
+ Context.BIND_WAIVE_PRIORITY,
+ Context.BIND_IMPORTANT,
+ Context.BIND_ADJUST_WITH_ACTIVITY,
+ Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
+ Context.BIND_FOREGROUND_SERVICE,
+ Context.BIND_TREAT_LIKE_ACTIVITY,
+ Context.BIND_VISIBLE,
+ Context.BIND_SHOWING_UI,
+ Context.BIND_NOT_VISIBLE,
+ };
+ private static int[] BIND_PROTO_ENUMS = new int[] {
+ ConnectionRecordProto.AUTO_CREATE,
+ ConnectionRecordProto.DEBUG_UNBIND,
+ ConnectionRecordProto.NOT_FG,
+ ConnectionRecordProto.IMPORTANT_BG,
+ ConnectionRecordProto.ABOVE_CLIENT,
+ ConnectionRecordProto.ALLOW_OOM_MANAGEMENT,
+ ConnectionRecordProto.WAIVE_PRIORITY,
+ ConnectionRecordProto.IMPORTANT,
+ ConnectionRecordProto.ADJUST_WITH_ACTIVITY,
+ ConnectionRecordProto.FG_SERVICE_WHILE_AWAKE,
+ ConnectionRecordProto.FG_SERVICE,
+ ConnectionRecordProto.TREAT_LIKE_ACTIVITY,
+ ConnectionRecordProto.VISIBLE,
+ ConnectionRecordProto.SHOWING_UI,
+ ConnectionRecordProto.NOT_VISIBLE,
+ };
+
void dump(PrintWriter pw, String prefix) {
pw.println(prefix + "binding=" + binding);
if (activity != null) {
@@ -46,7 +83,7 @@
pw.println(prefix + "conn=" + conn.asBinder()
+ " flags=0x" + Integer.toHexString(flags));
}
-
+
ConnectionRecord(AppBindRecord _binding, ActivityRecord _activity,
IServiceConnection _conn, int _flags,
int _clientLabel, PendingIntent _clientIntent) {
@@ -131,51 +168,8 @@
if (binding.client != null) {
proto.write(ConnectionRecordProto.USER_ID, binding.client.userId);
}
- if ((flags&Context.BIND_AUTO_CREATE) != 0) {
- proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.AUTO_CREATE);
- }
- if ((flags&Context.BIND_DEBUG_UNBIND) != 0) {
- proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.DEBUG_UNBIND);
- }
- if ((flags&Context.BIND_NOT_FOREGROUND) != 0) {
- proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.NOT_FG);
- }
- if ((flags&Context.BIND_IMPORTANT_BACKGROUND) != 0) {
- proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.IMPORTANT_BG);
- }
- if ((flags&Context.BIND_ABOVE_CLIENT) != 0) {
- proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.ABOVE_CLIENT);
- }
- if ((flags&Context.BIND_ALLOW_OOM_MANAGEMENT) != 0) {
- proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.ALLOW_OOM_MANAGEMENT);
- }
- if ((flags&Context.BIND_WAIVE_PRIORITY) != 0) {
- proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.WAIVE_PRIORITY);
- }
- if ((flags&Context.BIND_IMPORTANT) != 0) {
- proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.IMPORTANT);
- }
- if ((flags&Context.BIND_ADJUST_WITH_ACTIVITY) != 0) {
- proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.ADJUST_WITH_ACTIVITY);
- }
- if ((flags&Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE) != 0) {
- proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.FG_SERVICE_WHILE_WAKE);
- }
- if ((flags&Context.BIND_FOREGROUND_SERVICE) != 0) {
- proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.FG_SERVICE);
- }
- if ((flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) {
- proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.TREAT_LIKE_ACTIVITY);
- }
- if ((flags&Context.BIND_VISIBLE) != 0) {
- proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.VISIBLE);
- }
- if ((flags&Context.BIND_SHOWING_UI) != 0) {
- proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.SHOWING_UI);
- }
- if ((flags&Context.BIND_NOT_VISIBLE) != 0) {
- proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.NOT_VISIBLE);
- }
+ ProtoUtils.writeBitWiseFlagsToProtoEnum(proto, ConnectionRecordProto.FLAGS,
+ flags, BIND_ORIG_ENUMS, BIND_PROTO_ENUMS);
if (serviceDead) {
proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.DEAD);
}
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 77f5c16..29bfebe 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -24,6 +24,7 @@
import java.nio.ByteBuffer;
import android.app.ActivityManager;
+import android.app.ActivityManagerProto;
import android.os.Build;
import android.os.SystemClock;
import com.android.internal.util.MemInfoReader;
@@ -416,6 +417,53 @@
return procState;
}
+ public static int makeProcStateProtoEnum(int curProcState) {
+ switch (curProcState) {
+ case ActivityManager.PROCESS_STATE_PERSISTENT:
+ return ActivityManagerProto.PROCESS_STATE_PERSISTENT;
+ case ActivityManager.PROCESS_STATE_PERSISTENT_UI:
+ return ActivityManagerProto.PROCESS_STATE_PERSISTENT_UI;
+ case ActivityManager.PROCESS_STATE_TOP:
+ return ActivityManagerProto.PROCESS_STATE_TOP;
+ case ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE:
+ return ActivityManagerProto.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+ case ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE:
+ return ActivityManagerProto.PROCESS_STATE_FOREGROUND_SERVICE;
+ case ActivityManager.PROCESS_STATE_TOP_SLEEPING:
+ return ActivityManagerProto.PROCESS_STATE_TOP_SLEEPING;
+ case ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND:
+ return ActivityManagerProto.PROCESS_STATE_IMPORTANT_FOREGROUND;
+ case ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND:
+ return ActivityManagerProto.PROCESS_STATE_IMPORTANT_BACKGROUND;
+ case ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND:
+ return ActivityManagerProto.PROCESS_STATE_TRANSIENT_BACKGROUND;
+ case ActivityManager.PROCESS_STATE_BACKUP:
+ return ActivityManagerProto.PROCESS_STATE_BACKUP;
+ case ActivityManager.PROCESS_STATE_HEAVY_WEIGHT:
+ return ActivityManagerProto.PROCESS_STATE_HEAVY_WEIGHT;
+ case ActivityManager.PROCESS_STATE_SERVICE:
+ return ActivityManagerProto.PROCESS_STATE_SERVICE;
+ case ActivityManager.PROCESS_STATE_RECEIVER:
+ return ActivityManagerProto.PROCESS_STATE_RECEIVER;
+ case ActivityManager.PROCESS_STATE_HOME:
+ return ActivityManagerProto.PROCESS_STATE_HOME;
+ case ActivityManager.PROCESS_STATE_LAST_ACTIVITY:
+ return ActivityManagerProto.PROCESS_STATE_LAST_ACTIVITY;
+ case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY:
+ return ActivityManagerProto.PROCESS_STATE_CACHED_ACTIVITY;
+ case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
+ return ActivityManagerProto.PROCESS_STATE_CACHED_ACTIVITY_CLIENT;
+ case ActivityManager.PROCESS_STATE_CACHED_RECENT:
+ return ActivityManagerProto.PROCESS_STATE_CACHED_RECENT;
+ case ActivityManager.PROCESS_STATE_CACHED_EMPTY:
+ return ActivityManagerProto.PROCESS_STATE_CACHED_EMPTY;
+ case ActivityManager.PROCESS_STATE_NONEXISTENT:
+ return ActivityManagerProto.PROCESS_STATE_NONEXISTENT;
+ default:
+ return -1;
+ }
+ }
+
public static void appendRamKb(StringBuilder sb, long ramKb) {
for (int j=0, fact=10; j<6; j++, fact*=10) {
if (ramKb < fact) {
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index a1e5947..03e140d 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -679,6 +679,7 @@
proto.write(ProcessRecordProto.ISOLATED_APP_ID, UserHandle.getAppId(uid));
}
}
+ proto.write(ProcessRecordProto.PERSISTENT, persistent);
proto.end(token);
}
diff --git a/services/core/java/com/android/server/am/UidRecord.java b/services/core/java/com/android/server/am/UidRecord.java
index 8efcb4f..3886e5a 100644
--- a/services/core/java/com/android/server/am/UidRecord.java
+++ b/services/core/java/com/android/server/am/UidRecord.java
@@ -18,13 +18,17 @@
import android.Manifest;
import android.app.ActivityManager;
+import android.app.ActivityManagerProto;
import android.content.pm.PackageManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
+import android.util.proto.ProtoUtils;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.am.proto.UidRecordProto;
/**
* Overall information about a uid that has actively running processes.
@@ -86,6 +90,22 @@
static final int CHANGE_CACHED = 1<<3;
static final int CHANGE_UNCACHED = 1<<4;
+ // Keep the enum lists in sync
+ private static int[] ORIG_ENUMS = new int[] {
+ CHANGE_GONE,
+ CHANGE_IDLE,
+ CHANGE_ACTIVE,
+ CHANGE_CACHED,
+ CHANGE_UNCACHED,
+ };
+ private static int[] PROTO_ENUMS = new int[] {
+ UidRecordProto.CHANGE_GONE,
+ UidRecordProto.CHANGE_IDLE,
+ UidRecordProto.CHANGE_ACTIVE,
+ UidRecordProto.CHANGE_CACHED,
+ UidRecordProto.CHANGE_UNCACHED,
+ };
+
static final class ChangeItem {
UidRecord uidRecord;
int uid;
@@ -125,6 +145,34 @@
}
}
+
+ void writeToProto(ProtoOutputStream proto, long fieldId) {
+ long token = proto.start(fieldId);
+ proto.write(UidRecordProto.HEX_HASH, Integer.toHexString(System.identityHashCode(this)));
+ proto.write(UidRecordProto.UID, uid);
+ proto.write(UidRecordProto.CURRENT, ProcessList.makeProcStateProtoEnum(curProcState));
+ proto.write(UidRecordProto.EPHEMERAL, ephemeral);
+ proto.write(UidRecordProto.FG_SERVICES, foregroundServices);
+ proto.write(UidRecordProto.WHILELIST, curWhitelist);
+ ProtoUtils.toDuration(proto, UidRecordProto.LAST_BACKGROUND_TIME,
+ lastBackgroundTime, SystemClock.elapsedRealtime());
+ proto.write(UidRecordProto.IDLE, idle);
+ if (lastReportedChange != 0) {
+ ProtoUtils.writeBitWiseFlagsToProtoEnum(proto, UidRecordProto.LAST_REPORTED_CHANGES,
+ lastReportedChange, ORIG_ENUMS, PROTO_ENUMS);
+ }
+ proto.write(UidRecordProto.NUM_PROCS, numProcs);
+
+ long seqToken = proto.start(UidRecordProto.NETWORK_STATE_UPDATE);
+ proto.write(UidRecordProto.ProcStateSequence.CURURENT, curProcStateSeq);
+ proto.write(UidRecordProto.ProcStateSequence.LAST_NETWORK_UPDATED,
+ lastNetworkUpdatedProcStateSeq);
+ proto.write(UidRecordProto.ProcStateSequence.LAST_DISPATCHED, lastDispatchedProcStateSeq);
+ proto.end(seqToken);
+
+ proto.end(token);
+ }
+
public String toString() {
StringBuilder sb = new StringBuilder(128);
sb.append("UidRecord{");
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 5ada484..7b0c714 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -83,6 +83,7 @@
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.TimingsTraceLog;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
@@ -94,6 +95,7 @@
import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.SystemServiceManager;
+import com.android.server.am.proto.UserControllerProto;
import com.android.server.pm.UserManagerService;
import com.android.server.wm.WindowManagerService;
@@ -1844,6 +1846,36 @@
}
}
+ void writeToProto(ProtoOutputStream proto, long fieldId) {
+ synchronized (mLock) {
+ long token = proto.start(fieldId);
+ for (int i = 0; i < mStartedUsers.size(); i++) {
+ UserState uss = mStartedUsers.valueAt(i);
+ final long uToken = proto.start(UserControllerProto.STARTED_USERS);
+ proto.write(UserControllerProto.User.ID, uss.mHandle.getIdentifier());
+ uss.writeToProto(proto, UserControllerProto.User.STATE);
+ proto.end(uToken);
+ }
+ for (int i = 0; i < mStartedUserArray.length; i++) {
+ proto.write(UserControllerProto.STARTED_USER_ARRAY, mStartedUserArray[i]);
+ }
+ for (int i = 0; i < mUserLru.size(); i++) {
+ proto.write(UserControllerProto.USER_LRU, mUserLru.get(i));
+ }
+ if (mUserProfileGroupIds.size() > 0) {
+ for (int i = 0; i < mUserProfileGroupIds.size(); i++) {
+ final long uToken = proto.start(UserControllerProto.USER_PROFILE_GROUP_IDS);
+ proto.write(UserControllerProto.UserProfile.USER,
+ mUserProfileGroupIds.keyAt(i));
+ proto.write(UserControllerProto.UserProfile.PROFILE,
+ mUserProfileGroupIds.valueAt(i));
+ proto.end(uToken);
+ }
+ }
+ proto.end(token);
+ }
+ }
+
void dump(PrintWriter pw, boolean dumpAll) {
synchronized (mLock) {
pw.println(" mStartedUsers:");
@@ -1868,10 +1900,6 @@
pw.print(mUserLru.get(i));
}
pw.println("]");
- if (dumpAll) {
- pw.print(" mStartedUserArray: ");
- pw.println(Arrays.toString(mStartedUserArray));
- }
if (mUserProfileGroupIds.size() > 0) {
pw.println(" mUserProfileGroupIds:");
for (int i=0; i< mUserProfileGroupIds.size(); i++) {
diff --git a/services/core/java/com/android/server/am/UserState.java b/services/core/java/com/android/server/am/UserState.java
index d36d9cb..00597e2 100644
--- a/services/core/java/com/android/server/am/UserState.java
+++ b/services/core/java/com/android/server/am/UserState.java
@@ -24,8 +24,10 @@
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.util.ProgressReporter;
+import com.android.server.am.proto.UserStateProto;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -112,10 +114,29 @@
}
}
+ public static int stateToProtoEnum(int state) {
+ switch (state) {
+ case STATE_BOOTING: return UserStateProto.STATE_BOOTING;
+ case STATE_RUNNING_LOCKED: return UserStateProto.STATE_RUNNING_LOCKED;
+ case STATE_RUNNING_UNLOCKING: return UserStateProto.STATE_RUNNING_UNLOCKING;
+ case STATE_RUNNING_UNLOCKED: return UserStateProto.STATE_RUNNING_UNLOCKED;
+ case STATE_STOPPING: return UserStateProto.STATE_STOPPING;
+ case STATE_SHUTDOWN: return UserStateProto.STATE_SHUTDOWN;
+ default: return state;
+ }
+ }
+
void dump(String prefix, PrintWriter pw) {
pw.print(prefix);
pw.print("state="); pw.print(stateToString(state));
if (switching) pw.print(" SWITCHING");
pw.println();
}
+
+ void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(UserStateProto.STATE, stateToProtoEnum(state));
+ proto.write(UserStateProto.SWITCHING, switching);
+ proto.end(token);
+ }
}
diff --git a/services/core/java/com/android/server/am/VrController.java b/services/core/java/com/android/server/am/VrController.java
index feddfe3..d32db7e 100644
--- a/services/core/java/com/android/server/am/VrController.java
+++ b/services/core/java/com/android/server/am/VrController.java
@@ -20,7 +20,11 @@
import android.os.Process;
import android.service.vr.IPersistentVrStateCallbacks;
import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
+import android.util.proto.ProtoUtils;
+
import com.android.server.LocalServices;
+import com.android.server.am.proto.ProcessesProto.VrControllerProto;
import com.android.server.vr.VrManagerInternal;
/**
@@ -49,6 +53,18 @@
private static final int FLAG_VR_MODE = 1;
private static final int FLAG_PERSISTENT_VR_MODE = 2;
+ // Keep the enum lists in sync
+ private static int[] ORIG_ENUMS = new int[] {
+ FLAG_NON_VR_MODE,
+ FLAG_VR_MODE,
+ FLAG_PERSISTENT_VR_MODE,
+ };
+ private static int[] PROTO_ENUMS = new int[] {
+ VrControllerProto.FLAG_NON_VR_MODE,
+ VrControllerProto.FLAG_VR_MODE,
+ VrControllerProto.FLAG_PERSISTENT_VR_MODE,
+ };
+
// Invariants maintained for mVrState
//
// Always true:
@@ -420,4 +436,12 @@
public String toString() {
return String.format("[VrState=0x%x,VrRenderThreadTid=%d]", mVrState, mVrRenderThreadTid);
}
+
+ void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ ProtoUtils.writeBitWiseFlagsToProtoEnum(proto, VrControllerProto.VR_MODE,
+ mVrState, ORIG_ENUMS, PROTO_ENUMS);
+ proto.write(VrControllerProto.RENDER_THREAD_ID, mVrRenderThreadTid);
+ proto.end(token);
+ }
}
diff --git a/services/core/java/com/android/server/fingerprint/AuthenticationClient.java b/services/core/java/com/android/server/fingerprint/AuthenticationClient.java
index 370e569..d30b13c 100644
--- a/services/core/java/com/android/server/fingerprint/AuthenticationClient.java
+++ b/services/core/java/com/android/server/fingerprint/AuthenticationClient.java
@@ -16,18 +16,22 @@
package com.android.server.fingerprint;
-import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-
import android.content.Context;
+import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintDialog;
import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.fingerprint.IFingerprintDialogReceiver;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
+import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.statusbar.IStatusBarService;
+
/**
* A class to keep track of the authentication state for a given client.
*/
@@ -41,11 +45,99 @@
public static final int LOCKOUT_TIMED = 1;
public static final int LOCKOUT_PERMANENT = 2;
+ // Callback mechanism received from the client
+ // (FingerprintDialog -> FingerprintManager -> FingerprintService -> AuthenticationClient)
+ private IFingerprintDialogReceiver mDialogReceiverFromClient;
+ private Bundle mBundle;
+ private IStatusBarService mStatusBarService;
+ private boolean mInLockout;
+ private final FingerprintManager mFingerprintManager;
+ protected boolean mDialogDismissed;
+
+ // Receives events from SystemUI
+ protected IFingerprintDialogReceiver mDialogReceiver = new IFingerprintDialogReceiver.Stub() {
+ @Override // binder call
+ public void onDialogDismissed(int reason) {
+ if (mBundle != null && mDialogReceiverFromClient != null) {
+ try {
+ mDialogReceiverFromClient.onDialogDismissed(reason);
+ if (reason == FingerprintDialog.DISMISSED_REASON_USER_CANCEL) {
+ onError(FingerprintManager.FINGERPRINT_ERROR_USER_CANCELED,
+ 0 /* vendorCode */);
+ }
+ mDialogDismissed = true;
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to notify dialog dismissed", e);
+ }
+ stop(true /* initiatedByClient */);
+ }
+ }
+ };
+
public AuthenticationClient(Context context, long halDeviceId, IBinder token,
IFingerprintServiceReceiver receiver, int targetUserId, int groupId, long opId,
- boolean restricted, String owner) {
+ boolean restricted, String owner, Bundle bundle,
+ IFingerprintDialogReceiver dialogReceiver, IStatusBarService statusBarService) {
super(context, halDeviceId, token, receiver, targetUserId, groupId, restricted, owner);
mOpId = opId;
+ mBundle = bundle;
+ mDialogReceiverFromClient = dialogReceiver;
+ mStatusBarService = statusBarService;
+ mFingerprintManager = (FingerprintManager) getContext()
+ .getSystemService(Context.FINGERPRINT_SERVICE);
+ }
+
+ @Override
+ public void binderDied() {
+ super.binderDied();
+ // When the binder dies, we should stop the client. This probably belongs in
+ // ClientMonitor's binderDied(), but testing all the cases would be tricky.
+ // AuthenticationClient is the most user-visible case.
+ stop(false /* initiatedByClient */);
+ }
+
+ @Override
+ public boolean onAcquired(int acquiredInfo, int vendorCode) {
+ // If the dialog is showing, the client doesn't need to receive onAcquired messages.
+ if (mBundle != null) {
+ try {
+ if (acquiredInfo != FingerprintManager.FINGERPRINT_ACQUIRED_GOOD) {
+ mStatusBarService.onFingerprintHelp(
+ mFingerprintManager.getAcquiredString(acquiredInfo, vendorCode));
+ }
+ return false; // acquisition continues
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception when sending acquired message", e);
+ return true; // client failed
+ } finally {
+ // Good scans will keep the device awake
+ if (acquiredInfo == FingerprintManager.FINGERPRINT_ACQUIRED_GOOD) {
+ notifyUserActivity();
+ }
+ }
+ } else {
+ return super.onAcquired(acquiredInfo, vendorCode);
+ }
+ }
+
+ @Override
+ public boolean onError(int error, int vendorCode) {
+ if (mDialogDismissed) {
+ // If user cancels authentication, the application has already received the
+ // FingerprintManager.FINGERPRINT_ERROR_USER_CANCELED message from onDialogDismissed()
+ // and stopped the fingerprint hardware, so there is no need to send a
+ // FingerprintManager.FINGERPRINT_ERROR_CANCELED message.
+ return true;
+ }
+ if (mBundle != null) {
+ try {
+ mStatusBarService.onFingerprintError(
+ mFingerprintManager.getErrorString(error, vendorCode));
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception when sending error", e);
+ }
+ }
+ return super.onError(error, vendorCode);
}
@Override
@@ -53,6 +145,20 @@
boolean result = false;
boolean authenticated = fingerId != 0;
+ // If the fingerprint dialog is showing, notify authentication succeeded
+ if (mBundle != null) {
+ try {
+ if (authenticated) {
+ mStatusBarService.onFingerprintAuthenticated();
+ } else {
+ mStatusBarService.onFingerprintHelp(getContext().getResources().getString(
+ com.android.internal.R.string.fingerprint_not_recognized));
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to notify Authenticated:", e);
+ }
+ }
+
IFingerprintServiceReceiver receiver = getReceiver();
if (receiver != null) {
try {
@@ -85,13 +191,24 @@
int lockoutMode = handleFailedAttempt();
if (lockoutMode != LOCKOUT_NONE) {
try {
+ mInLockout = true;
Slog.w(TAG, "Forcing lockout (fp driver code should do this!), mode(" +
lockoutMode + ")");
stop(false);
int errorCode = lockoutMode == LOCKOUT_TIMED ?
FingerprintManager.FINGERPRINT_ERROR_LOCKOUT :
FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT;
+
+ // TODO: if the dialog is showing, this error should be delayed. On a similar
+ // note, AuthenticationClient should override onError and delay all other errors
+ // as well, if the dialog is showing
receiver.onError(getHalDeviceId(), errorCode, 0 /* vendorCode */);
+
+ // Send the lockout message to the system dialog
+ if (mBundle != null) {
+ mStatusBarService.onFingerprintError(
+ mFingerprintManager.getErrorString(errorCode, 0 /* vendorCode */));
+ }
} catch (RemoteException e) {
Slog.w(TAG, "Failed to notify lockout:", e);
}
@@ -126,6 +243,15 @@
return result;
}
if (DEBUG) Slog.w(TAG, "client " + getOwnerString() + " is authenticating...");
+
+ // If authenticating with system dialog, show the dialog
+ if (mBundle != null) {
+ try {
+ mStatusBarService.showFingerprintDialog(mBundle, mDialogReceiver);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to show fingerprint dialog", e);
+ }
+ }
} catch (RemoteException e) {
Slog.e(TAG, "startAuthentication failed", e);
return ERROR_ESRCH;
@@ -139,6 +265,7 @@
Slog.w(TAG, "stopAuthentication: already cancelled!");
return 0;
}
+
IBiometricsFingerprint daemon = getFingerprintDaemon();
if (daemon == null) {
Slog.w(TAG, "stopAuthentication: no fingerprint HAL!");
@@ -154,6 +281,18 @@
} catch (RemoteException e) {
Slog.e(TAG, "stopAuthentication failed", e);
return ERROR_ESRCH;
+ } finally {
+ // If the user already cancelled authentication (via some interaction with the
+ // dialog, we do not need to hide it since it's already hidden.
+ // If the device is in lockout, don't hide the dialog - it will automatically hide
+ // after FingerprintDialog.HIDE_DIALOG_DELAY
+ if (mBundle != null && !mDialogDismissed && !mInLockout) {
+ try {
+ mStatusBarService.hideFingerprintDialog();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to hide fingerprint dialog", e);
+ }
+ }
}
mAlreadyCancelled = true;
return 0; // success
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index d0d951b..3e1958d 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -40,6 +40,7 @@
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.IFingerprintClientActiveCallback;
+import android.hardware.fingerprint.IFingerprintDialogReceiver;
import android.hardware.fingerprint.IFingerprintService;
import android.hardware.fingerprint.IFingerprintServiceLockoutResetCallback;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
@@ -55,6 +56,7 @@
import android.os.PowerManager.WakeLock;
import android.os.RemoteException;
import android.os.SELinux;
+import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
@@ -66,6 +68,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
+import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.DumpUtils;
import com.android.server.SystemServerInitThreadPool;
import com.android.server.SystemService;
@@ -131,6 +134,7 @@
private SparseIntArray mFailedAttempts;
@GuardedBy("this")
private IBiometricsFingerprint mDaemon;
+ private IStatusBarService mStatusBarService;
private final PowerManager mPowerManager;
private final AlarmManager mAlarmManager;
private final UserManager mUserManager;
@@ -222,6 +226,8 @@
mUserManager = UserManager.get(mContext);
mTimedLockoutCleared = new SparseBooleanArray();
mFailedAttempts = new SparseIntArray();
+ mStatusBarService = IStatusBarService.Stub.asInterface(
+ ServiceManager.getService(Context.STATUS_BAR_SERVICE));
}
@Override
@@ -808,13 +814,14 @@
private void startAuthentication(IBinder token, long opId, int callingUserId, int groupId,
IFingerprintServiceReceiver receiver, int flags, boolean restricted,
- String opPackageName) {
+ String opPackageName, Bundle bundle, IFingerprintDialogReceiver dialogReceiver) {
updateActiveGroup(groupId, opPackageName);
if (DEBUG) Slog.v(TAG, "startAuthentication(" + opPackageName + ")");
AuthenticationClient client = new AuthenticationClient(getContext(), mHalDeviceId, token,
- receiver, mCurrentUserId, groupId, opId, restricted, opPackageName) {
+ receiver, mCurrentUserId, groupId, opId, restricted, opPackageName, bundle,
+ dialogReceiver, mStatusBarService) {
@Override
public int handleFailedAttempt() {
final int currentUser = ActivityManager.getCurrentUser();
@@ -1037,7 +1044,7 @@
final IFingerprintServiceReceiver receiver, final int flags,
final String opPackageName) {
checkPermission(MANAGE_FINGERPRINT);
- final int limit = mContext.getResources().getInteger(
+ final int limit = mContext.getResources().getInteger(
com.android.internal.R.integer.config_fingerprintMaxTemplatesPerUser);
final int enrolled = FingerprintService.this.getEnrolledFingerprints(userId).size();
@@ -1085,7 +1092,8 @@
@Override // Binder call
public void authenticate(final IBinder token, final long opId, final int groupId,
final IFingerprintServiceReceiver receiver, final int flags,
- final String opPackageName) {
+ final String opPackageName, final Bundle bundle,
+ final IFingerprintDialogReceiver dialogReceiver) {
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
final int callingUserId = UserHandle.getCallingUserId();
@@ -1113,7 +1121,7 @@
mPerformanceStats = stats;
startAuthentication(token, opId, callingUserId, groupId, receiver,
- flags, restricted, opPackageName);
+ flags, restricted, opPackageName, bundle, dialogReceiver);
}
});
}
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index fa5fdf5..5da470e 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -21,6 +21,7 @@
import android.app.Activity;
import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
import android.app.AppGlobals;
import android.app.IUidObserver;
import android.app.job.IJobScheduler;
@@ -73,8 +74,10 @@
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
+import com.android.internal.util.Preconditions;
import com.android.server.DeviceIdleController;
import com.android.server.FgThread;
+import com.android.server.ForceAppStandbyTracker;
import com.android.server.LocalServices;
import com.android.server.job.JobSchedulerServiceDumpProto.ActiveJob;
import com.android.server.job.JobSchedulerServiceDumpProto.PendingJob;
@@ -102,6 +105,7 @@
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
+import java.util.function.Predicate;
/**
* Responsible for taking jobs representing work to be performed by a client app, and determining
@@ -175,8 +179,10 @@
final JobSchedulerStub mJobSchedulerStub;
PackageManagerInternal mLocalPM;
+ ActivityManagerInternal mActivityManagerInternal;
IBatteryStats mBatteryStats;
DeviceIdleController.LocalService mLocalDeviceIdleController;
+ final ForceAppStandbyTracker mForceAppStandbyTracker;
/**
* Set to true once we are allowed to run third party apps.
@@ -778,6 +784,22 @@
mStartedUsers = ArrayUtils.removeInt(mStartedUsers, userHandle);
}
+ /**
+ * Return whether an UID is in the foreground or not.
+ */
+ private boolean isUidInForeground(int uid) {
+ synchronized (mLock) {
+ if (mUidPriorityOverride.get(uid, 0) > 0) {
+ return true;
+ }
+ }
+ // Note UID observer may not be called in time, so we always check with the AM.
+ return mActivityManagerInternal.getUidProcessState(uid)
+ <= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+ }
+
+ private final Predicate<Integer> mIsUidInForegroundPredicate = this::isUidInForeground;
+
public int scheduleAsPackage(JobInfo job, JobWorkItem work, int uId, String packageName,
int userId, String tag) {
try {
@@ -797,12 +819,25 @@
// Fast path: we are adding work to an existing job, and the JobInfo is not
// changing. We can just directly enqueue this work in to the job.
if (toCancel.getJob().equals(job)) {
+
toCancel.enqueueWorkLocked(ActivityManager.getService(), work);
+
+ // If any of work item is enqueued when the source is in the foreground,
+ // exempt the entire job.
+ toCancel.maybeAddForegroundExemption(mIsUidInForegroundPredicate);
+
return JobScheduler.RESULT_SUCCESS;
}
}
JobStatus jobStatus = JobStatus.createFromJobInfo(job, uId, packageName, userId, tag);
+
+ // Give exemption if the source is in the foreground just now.
+ // Note if it's a sync job, this method is called on the handler so it's not exactly
+ // the state when requestSync() was called, but that should be fine because of the
+ // 1 minute foreground grace period.
+ jobStatus.maybeAddForegroundExemption(mIsUidInForegroundPredicate);
+
if (DEBUG) Slog.d(TAG, "SCHEDULE: " + jobStatus.toShortString());
// Jobs on behalf of others don't apply to the per-app job cap
if (ENFORCE_MAX_JOBS && packageName == null) {
@@ -1047,6 +1082,8 @@
super(context);
mLocalPM = LocalServices.getService(PackageManagerInternal.class);
+ mActivityManagerInternal = Preconditions.checkNotNull(
+ LocalServices.getService(ActivityManagerInternal.class));
mHandler = new JobHandler(context.getMainLooper());
mConstants = new Constants(mHandler);
@@ -1078,6 +1115,8 @@
mDeviceIdleJobsController = DeviceIdleJobsController.get(this);
mControllers.add(mDeviceIdleJobsController);
+ mForceAppStandbyTracker = ForceAppStandbyTracker.getInstance(context);
+
// If the job store determined that it can't yet reschedule persisted jobs,
// we need to start watching the clock.
if (!mJobs.jobTimesInflatedValid()) {
@@ -1137,6 +1176,9 @@
public void onBootPhase(int phase) {
if (PHASE_SYSTEM_SERVICES_READY == phase) {
mConstants.start(getContext().getContentResolver());
+
+ mForceAppStandbyTracker.start();
+
// Register br for package removals and user removals.
final IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
diff --git a/services/core/java/com/android/server/job/JobStore.java b/services/core/java/com/android/server/job/JobStore.java
index 36cacd7a..6da783c 100644
--- a/services/core/java/com/android/server/job/JobStore.java
+++ b/services/core/java/com/android/server/job/JobStore.java
@@ -72,6 +72,9 @@
* This is important b/c {@link com.android.server.job.JobStore.WriteJobsMapToDiskRunnable}
* and {@link com.android.server.job.JobStore.ReadJobMapFromDiskRunnable} lock on that
* object.
+ *
+ * Test:
+ * atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
*/
public final class JobStore {
private static final String TAG = "JobStore";
@@ -427,6 +430,9 @@
out.attribute(null, "uid", Integer.toString(jobStatus.getUid()));
out.attribute(null, "priority", String.valueOf(jobStatus.getPriority()));
out.attribute(null, "flags", String.valueOf(jobStatus.getFlags()));
+ if (jobStatus.getInternalFlags() != 0) {
+ out.attribute(null, "internalFlags", String.valueOf(jobStatus.getInternalFlags()));
+ }
out.attribute(null, "lastSuccessfulRunTime",
String.valueOf(jobStatus.getLastSuccessfulRunTime()));
@@ -689,6 +695,7 @@
int uid, sourceUserId;
long lastSuccessfulRunTime;
long lastFailedRunTime;
+ int internalFlags = 0;
// Read out job identifier attributes and priority.
try {
@@ -704,6 +711,10 @@
if (val != null) {
jobBuilder.setFlags(Integer.parseInt(val));
}
+ val = parser.getAttributeValue(null, "internalFlags");
+ if (val != null) {
+ internalFlags = Integer.parseInt(val);
+ }
val = parser.getAttributeValue(null, "sourceUserId");
sourceUserId = val == null ? -1 : Integer.parseInt(val);
@@ -718,7 +729,6 @@
}
String sourcePackageName = parser.getAttributeValue(null, "sourcePackageName");
-
final String sourceTag = parser.getAttributeValue(null, "sourceTag");
int eventType;
@@ -857,7 +867,7 @@
appBucket, currentHeartbeat, sourceTag,
elapsedRuntimes.first, elapsedRuntimes.second,
lastSuccessfulRunTime, lastFailedRunTime,
- (rtcIsGood) ? null : rtcRuntimes);
+ (rtcIsGood) ? null : rtcRuntimes, internalFlags);
return js;
}
diff --git a/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java b/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java
index 1d053a5..2e4567a 100644
--- a/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java
+++ b/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java
@@ -197,7 +197,9 @@
final int uid = jobStatus.getSourceUid();
final String packageName = jobStatus.getSourcePackageName();
- final boolean canRun = !mForceAppStandbyTracker.areJobsRestricted(uid, packageName);
+ final boolean canRun = !mForceAppStandbyTracker.areJobsRestricted(uid, packageName,
+ (jobStatus.getInternalFlags() & JobStatus.INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION)
+ != 0);
return jobStatus.setBackgroundNotRestrictedConstraintSatisfied(canRun);
}
diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java
index 611f4ec..d9a5ff6 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -46,6 +46,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.function.Predicate;
/**
* Uniquely identifies a job internally.
@@ -184,6 +185,21 @@
*/
private int trackingControllers;
+ /**
+ * Flag for {@link #mInternalFlags}: this job was scheduled when the app that owns the job
+ * service (not necessarily the caller) was in the foreground and the job has no time
+ * constraints, which makes it exempted from the battery saver job restriction.
+ *
+ * @hide
+ */
+ public static final int INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION = 1 << 0;
+
+ /**
+ * Versatile, persistable flags for a job that's updated within the system server,
+ * as opposed to {@link JobInfo#flags} that's set by callers.
+ */
+ private int mInternalFlags;
+
// These are filled in by controllers when preparing for execution.
public ArraySet<Uri> changedUris;
public ArraySet<String> changedAuthorities;
@@ -248,7 +264,7 @@
private JobStatus(JobInfo job, int callingUid, int targetSdkVersion, String sourcePackageName,
int sourceUserId, int standbyBucket, long heartbeat, String tag, int numFailures,
long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis,
- long lastSuccessfulRunTime, long lastFailedRunTime) {
+ long lastSuccessfulRunTime, long lastFailedRunTime, int internalFlags) {
this.job = job;
this.callingUid = callingUid;
this.targetSdkVersion = targetSdkVersion;
@@ -304,6 +320,8 @@
mLastSuccessfulRunTime = lastSuccessfulRunTime;
mLastFailedRunTime = lastFailedRunTime;
+ mInternalFlags = internalFlags;
+
updateEstimatedNetworkBytesLocked();
}
@@ -315,7 +333,8 @@
jobStatus.getStandbyBucket(), jobStatus.getBaseHeartbeat(),
jobStatus.getSourceTag(), jobStatus.getNumFailures(),
jobStatus.getEarliestRunTime(), jobStatus.getLatestRunTimeElapsed(),
- jobStatus.getLastSuccessfulRunTime(), jobStatus.getLastFailedRunTime());
+ jobStatus.getLastSuccessfulRunTime(), jobStatus.getLastFailedRunTime(),
+ jobStatus.getInternalFlags());
mPersistedUtcTimes = jobStatus.mPersistedUtcTimes;
if (jobStatus.mPersistedUtcTimes != null) {
if (DEBUG) {
@@ -336,12 +355,13 @@
int standbyBucket, long baseHeartbeat, String sourceTag,
long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis,
long lastSuccessfulRunTime, long lastFailedRunTime,
- Pair<Long, Long> persistedExecutionTimesUTC) {
+ Pair<Long, Long> persistedExecutionTimesUTC,
+ int innerFlags) {
this(job, callingUid, resolveTargetSdkVersion(job), sourcePkgName, sourceUserId,
standbyBucket, baseHeartbeat,
sourceTag, 0,
earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis,
- lastSuccessfulRunTime, lastFailedRunTime);
+ lastSuccessfulRunTime, lastFailedRunTime, innerFlags);
// Only during initial inflation do we record the UTC-timebase execution bounds
// read from the persistent store. If we ever have to recreate the JobStatus on
@@ -365,7 +385,7 @@
rescheduling.getStandbyBucket(), newBaseHeartbeat,
rescheduling.getSourceTag(), backoffAttempt, newEarliestRuntimeElapsedMillis,
newLatestRuntimeElapsedMillis,
- lastSuccessfulRunTime, lastFailedRunTime);
+ lastSuccessfulRunTime, lastFailedRunTime, rescheduling.getInternalFlags());
}
/**
@@ -395,10 +415,12 @@
sourceUserId, elapsedNow);
JobSchedulerInternal js = LocalServices.getService(JobSchedulerInternal.class);
long currentHeartbeat = js != null ? js.currentHeartbeat() : 0;
+
return new JobStatus(job, callingUid, resolveTargetSdkVersion(job), sourcePkg, sourceUserId,
standbyBucket, currentHeartbeat, tag, 0,
earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis,
- 0 /* lastSuccessfulRunTime */, 0 /* lastFailedRunTime */);
+ 0 /* lastSuccessfulRunTime */, 0 /* lastFailedRunTime */,
+ /*innerFlags=*/ 0);
}
public void enqueueWorkLocked(IActivityManager am, JobWorkItem work) {
@@ -623,6 +645,28 @@
return job.getFlags();
}
+ public int getInternalFlags() {
+ return mInternalFlags;
+ }
+
+ public void addInternalFlags(int flags) {
+ mInternalFlags |= flags;
+ }
+
+ public void maybeAddForegroundExemption(Predicate<Integer> uidForegroundChecker) {
+ // Jobs with time constraints shouldn't be exempted.
+ if (job.hasEarlyConstraint() || job.hasLateConstraint()) {
+ return;
+ }
+ // Already exempted, skip the foreground check.
+ if ((mInternalFlags & INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION) != 0) {
+ return;
+ }
+ if (uidForegroundChecker.test(getSourceUid())) {
+ addInternalFlags(INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION);
+ }
+ }
+
private void updateEstimatedNetworkBytesLocked() {
totalNetworkBytes = computeEstimatedNetworkBytesLocked();
}
@@ -1170,6 +1214,15 @@
pw.print(prefix); pw.print(" Flags: ");
pw.println(Integer.toHexString(job.getFlags()));
}
+ if (getInternalFlags() != 0) {
+ pw.print(prefix); pw.print(" Internal flags: ");
+ pw.print(Integer.toHexString(getInternalFlags()));
+
+ if ((getInternalFlags()&INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION) != 0) {
+ pw.print(" HAS_FOREGROUND_EXEMPTION");
+ }
+ pw.println();
+ }
pw.print(prefix); pw.print(" Requires: charging=");
pw.print(job.isRequireCharging()); pw.print(" batteryNotLow=");
pw.print(job.isRequireBatteryNotLow()); pw.print(" deviceIdle=");
@@ -1325,6 +1378,7 @@
proto.write(JobStatusDumpProto.SOURCE_UID, getSourceUid());
proto.write(JobStatusDumpProto.SOURCE_USER_ID, getSourceUserId());
proto.write(JobStatusDumpProto.SOURCE_PACKAGE_NAME, getSourcePackageName());
+ proto.write(JobStatusDumpProto.INTERNAL_FLAGS, getInternalFlags());
if (full) {
final long jiToken = proto.start(JobStatusDumpProto.JOB_INFO);
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 51abe9f..31c20cb 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -1598,8 +1598,10 @@
userId, progressCallback);
// The user employs synthetic password based credential.
if (response != null) {
- mRecoverableKeyStoreManager.lockScreenSecretAvailable(credentialType, credential,
- userId);
+ if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
+ mRecoverableKeyStoreManager.lockScreenSecretAvailable(credentialType, credential,
+ userId);
+ }
return response;
}
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 6812778..c9c7d04 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -38,6 +38,7 @@
import android.media.IAudioService;
import android.media.IMediaSession2;
import android.media.IRemoteVolumeController;
+import android.media.MediaLibraryService2;
import android.media.MediaSessionService2;
import android.media.SessionToken;
import android.media.session.IActiveSessionsListener;
@@ -445,9 +446,18 @@
}
// TODO(jaewan): Query per users.
- List<ResolveInfo> services = getContext().getPackageManager().queryIntentServices(
- new Intent(MediaSessionService2.SERVICE_INTERFACE),
- PackageManager.GET_META_DATA);
+ List<ResolveInfo> services = new ArrayList<>();
+ // If multiple actions are declared for a service, browser gets higher priority.
+ List<ResolveInfo> libraryServices = getContext().getPackageManager().queryIntentServices(
+ new Intent(MediaLibraryService2.SERVICE_INTERFACE), PackageManager.GET_META_DATA);
+ if (libraryServices != null) {
+ services.addAll(libraryServices);
+ }
+ List<ResolveInfo> sessionServices = getContext().getPackageManager().queryIntentServices(
+ new Intent(MediaSessionService2.SERVICE_INTERFACE), PackageManager.GET_META_DATA);
+ if (sessionServices != null) {
+ services.addAll(sessionServices);
+ }
synchronized (mLock) {
mSessions.clear();
if (services == null) {
@@ -470,10 +480,11 @@
+ " the same ID=" + id + ". Ignoring "
+ serviceInfo.packageName + "/" + serviceInfo.name);
} else {
+ int type = (libraryServices.contains(services.get(i)))
+ ? SessionToken.TYPE_LIBRARY_SERVICE : SessionToken.TYPE_SESSION_SERVICE;
MediaSessionService2Record record =
new MediaSessionService2Record(getContext(), mSessionDestroyedListener,
- SessionToken.TYPE_SESSION_SERVICE,
- serviceInfo.packageName, serviceInfo.name, id);
+ type, serviceInfo.packageName, serviceInfo.name, id);
mSessions.add(record);
}
}
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 78fd4b4..bfc150e 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -887,17 +887,21 @@
@Override
public long getUidStats(int uid, int type) {
- return nativeGetUidStat(uid, type);
+ return nativeGetUidStat(uid, type, checkBpfStatsEnable());
}
@Override
public long getIfaceStats(String iface, int type) {
- return nativeGetIfaceStat(iface, type);
+ return nativeGetIfaceStat(iface, type, checkBpfStatsEnable());
}
@Override
public long getTotalStats(int type) {
- return nativeGetTotalStat(type);
+ return nativeGetTotalStat(type, checkBpfStatsEnable());
+ }
+
+ private boolean checkBpfStatsEnable() {
+ return new File("/sys/fs/bpf/traffic_uid_stats_map").exists();
}
/**
@@ -1668,7 +1672,7 @@
private static int TYPE_TCP_RX_PACKETS;
private static int TYPE_TCP_TX_PACKETS;
- private static native long nativeGetTotalStat(int type);
- private static native long nativeGetIfaceStat(String iface, int type);
- private static native long nativeGetUidStat(int uid, int type);
+ private static native long nativeGetTotalStat(int type, boolean useBpfStats);
+ private static native long nativeGetIfaceStat(String iface, int type, boolean useBpfStats);
+ private static native long nativeGetUidStat(int uid, int type, boolean useBpfStats);
}
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 42093e8..502760a 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -254,13 +254,11 @@
for (ComponentName cmpt : mEnabledServicesForCurrentProfiles) {
if (filter != null && !filter.matches(cmpt)) continue;
-
cmpt.writeToProto(proto, ManagedServicesProto.ENABLED);
}
for (ManagedServiceInfo info : mServices) {
if (filter != null && !filter.matches(info.component)) continue;
-
info.writeToProto(proto, ManagedServicesProto.LIVE_SERVICES, this);
}
@@ -1145,13 +1143,11 @@
public void writeToProto(ProtoOutputStream proto, long fieldId, ManagedServices host) {
final long token = proto.start(fieldId);
-
component.writeToProto(proto, ManagedServiceInfoProto.COMPONENT);
proto.write(ManagedServiceInfoProto.USER_ID, userid);
proto.write(ManagedServiceInfoProto.SERVICE, service.getClass().getName());
proto.write(ManagedServiceInfoProto.IS_SYSTEM, isSystem);
proto.write(ManagedServiceInfoProto.IS_GUEST, isGuest(host));
-
proto.end(token);
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 39b7c7c..efe54fa 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -3382,36 +3382,28 @@
private void dumpProto(FileDescriptor fd, @NonNull DumpFilter filter) {
final ProtoOutputStream proto = new ProtoOutputStream(fd);
synchronized (mNotificationLock) {
- long records = proto.start(NotificationServiceDumpProto.RECORDS);
int N = mNotificationList.size();
- if (N > 0) {
- for (int i = 0; i < N; i++) {
- final NotificationRecord nr = mNotificationList.get(i);
- if (filter.filtered && !filter.matches(nr.sbn)) continue;
- nr.dump(proto, filter.redact);
- proto.write(NotificationRecordProto.STATE, NotificationRecordProto.POSTED);
- }
+ for (int i = 0; i < N; i++) {
+ final NotificationRecord nr = mNotificationList.get(i);
+ if (filter.filtered && !filter.matches(nr.sbn)) continue;
+ nr.dump(proto, NotificationServiceDumpProto.RECORDS, filter.redact,
+ NotificationRecordProto.POSTED);
}
N = mEnqueuedNotifications.size();
- if (N > 0) {
- for (int i = 0; i < N; i++) {
- final NotificationRecord nr = mEnqueuedNotifications.get(i);
- if (filter.filtered && !filter.matches(nr.sbn)) continue;
- nr.dump(proto, filter.redact);
- proto.write(NotificationRecordProto.STATE, NotificationRecordProto.ENQUEUED);
- }
+ for (int i = 0; i < N; i++) {
+ final NotificationRecord nr = mEnqueuedNotifications.get(i);
+ if (filter.filtered && !filter.matches(nr.sbn)) continue;
+ nr.dump(proto, NotificationServiceDumpProto.RECORDS, filter.redact,
+ NotificationRecordProto.ENQUEUED);
}
List<NotificationRecord> snoozed = mSnoozeHelper.getSnoozed();
N = snoozed.size();
- if (N > 0) {
- for (int i = 0; i < N; i++) {
- final NotificationRecord nr = snoozed.get(i);
- if (filter.filtered && !filter.matches(nr.sbn)) continue;
- nr.dump(proto, filter.redact);
- proto.write(NotificationRecordProto.STATE, NotificationRecordProto.SNOOZED);
- }
+ for (int i = 0; i < N; i++) {
+ final NotificationRecord nr = snoozed.get(i);
+ if (filter.filtered && !filter.matches(nr.sbn)) continue;
+ nr.dump(proto, NotificationServiceDumpProto.RECORDS, filter.redact,
+ NotificationRecordProto.SNOOZED);
}
- proto.end(records);
long zenLog = proto.start(NotificationServiceDumpProto.ZEN);
mZenModeHelper.dump(proto);
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index faa300f2..23b9743 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -361,8 +361,11 @@
/** @deprecated Use {@link #getUser()} instead. */
public int getUserId() { return sbn.getUserId(); }
- void dump(ProtoOutputStream proto, boolean redact) {
+ void dump(ProtoOutputStream proto, long fieldId, boolean redact, int state) {
+ final long token = proto.start(fieldId);
+
proto.write(NotificationRecordProto.KEY, sbn.getKey());
+ proto.write(NotificationRecordProto.STATE, state);
if (getChannel() != null) {
proto.write(NotificationRecordProto.CHANNEL_ID, getChannel().getId());
}
@@ -375,8 +378,10 @@
proto.write(NotificationRecordProto.SOUND, getSound().toString());
}
if (getAudioAttributes() != null) {
- proto.write(NotificationRecordProto.SOUND_USAGE, getAudioAttributes().getUsage());
+ getAudioAttributes().writeToProto(proto, NotificationRecordProto.AUDIO_ATTRIBUTES);
}
+
+ proto.end(token);
}
String formatRemoteViews(RemoteViews rv) {
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index c0dccb5..b0e3820 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -973,16 +973,11 @@
proto.write(RecordProto.VISIBILITY, r.visibility);
proto.write(RecordProto.SHOW_BADGE, r.showBadge);
- long token;
for (NotificationChannel channel : r.channels.values()) {
- token = proto.start(RecordProto.CHANNELS);
- channel.toProto(proto);
- proto.end(token);
+ channel.writeToProto(proto, RecordProto.CHANNELS);
}
for (NotificationChannelGroup group : r.groups.values()) {
- token = proto.start(RecordProto.CHANNEL_GROUPS);
- group.toProto(proto);
- proto.end(token);
+ group.writeToProto(proto, RecordProto.CHANNEL_GROUPS);
}
proto.end(fToken);
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 8f672b5..932e4f9 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -565,7 +565,7 @@
proto.write(ZenModeProto.ENABLED_ACTIVE_CONDITIONS, rule.toString());
}
}
- mConfig.toNotificationPolicy().toProto(proto, ZenModeProto.POLICY);
+ mConfig.toNotificationPolicy().writeToProto(proto, ZenModeProto.POLICY);
proto.write(ZenModeProto.SUPPRESSED_EFFECTS, mSuppressedEffects);
}
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index da1bdc7..42b6946 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -24,6 +24,8 @@
import static android.Manifest.permission.REQUEST_DELETE_PACKAGES;
import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
import static android.Manifest.permission.WRITE_MEDIA_STORAGE;
+import static android.content.pm.PackageManager.CERT_INPUT_RAW_X509;
+import static android.content.pm.PackageManager.CERT_INPUT_SHA256;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
@@ -108,6 +110,8 @@
import static com.android.server.pm.PackageManagerServiceUtils.getCompressedFiles;
import static com.android.server.pm.PackageManagerServiceUtils.getLastModifiedTime;
import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo;
+import static com.android.server.pm.PackageManagerServiceUtils.signingDetailsHasCertificate;
+import static com.android.server.pm.PackageManagerServiceUtils.signingDetailsHasSha256Certificate;
import static com.android.server.pm.PackageManagerServiceUtils.verifySignatures;
import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_FAILURE;
import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_SUCCESS;
@@ -5457,6 +5461,73 @@
}
}
+ @Override
+ public boolean hasSigningCertificate(
+ String packageName, byte[] certificate, @PackageManager.CertificateInputType int type) {
+
+ synchronized (mPackages) {
+ final PackageParser.Package p = mPackages.get(packageName);
+ if (p == null || p.mExtras == null) {
+ return false;
+ }
+ final int callingUid = Binder.getCallingUid();
+ final int callingUserId = UserHandle.getUserId(callingUid);
+ final PackageSetting ps = (PackageSetting) p.mExtras;
+ if (filterAppAccessLPr(ps, callingUid, callingUserId)) {
+ return false;
+ }
+ switch (type) {
+ case CERT_INPUT_RAW_X509:
+ return signingDetailsHasCertificate(certificate, p.mSigningDetails);
+ case CERT_INPUT_SHA256:
+ return signingDetailsHasSha256Certificate(certificate, p.mSigningDetails);
+ default:
+ return false;
+ }
+ }
+ }
+
+ @Override
+ public boolean hasUidSigningCertificate(
+ int uid, byte[] certificate, @PackageManager.CertificateInputType int type) {
+ final int callingUid = Binder.getCallingUid();
+ final int callingUserId = UserHandle.getUserId(callingUid);
+ // Map to base uids.
+ uid = UserHandle.getAppId(uid);
+ // reader
+ synchronized (mPackages) {
+ final PackageParser.SigningDetails signingDetails;
+ final Object obj = mSettings.getUserIdLPr(uid);
+ if (obj != null) {
+ if (obj instanceof SharedUserSetting) {
+ final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null;
+ if (isCallerInstantApp) {
+ return false;
+ }
+ signingDetails = ((SharedUserSetting)obj).signatures.mSigningDetails;
+ } else if (obj instanceof PackageSetting) {
+ final PackageSetting ps = (PackageSetting) obj;
+ if (filterAppAccessLPr(ps, callingUid, callingUserId)) {
+ return false;
+ }
+ signingDetails = ps.signatures.mSigningDetails;
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ switch (type) {
+ case CERT_INPUT_RAW_X509:
+ return signingDetailsHasCertificate(certificate, signingDetails);
+ case CERT_INPUT_SHA256:
+ return signingDetailsHasSha256Certificate(certificate, signingDetails);
+ default:
+ return false;
+ }
+ }
+ }
+
/**
* This method should typically only be used when granting or revoking
* permissions, since the app may immediately restart after this call.
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index cfc12de..021c4b8 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -54,6 +54,7 @@
import android.system.Os;
import android.util.ArraySet;
import android.util.Log;
+import android.util.PackageUtils;
import android.util.Slog;
import android.util.jar.StrictJarFile;
import android.util.proto.ProtoOutputStream;
@@ -74,6 +75,8 @@
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.text.SimpleDateFormat;
@@ -576,6 +579,69 @@
return true;
}
+
+ /**
+ * Checks the signing certificates to see if the provided certificate is a member. Invalid for
+ * {@code SigningDetails} with multiple signing certificates.
+ * @param certificate certificate to check for membership
+ * @param signingDetails signing certificates record whose members are to be searched
+ * @return true if {@code certificate} is in {@code signingDetails}
+ */
+ public static boolean signingDetailsHasCertificate(
+ byte[] certificate, PackageParser.SigningDetails signingDetails) {
+ if (signingDetails == PackageParser.SigningDetails.UNKNOWN) {
+ return false;
+ }
+ Signature signature = new Signature(certificate);
+ if (signingDetails.hasPastSigningCertificates()) {
+ for (int i = 0; i < signingDetails.pastSigningCertificates.length; i++) {
+ if (signingDetails.pastSigningCertificates[i].equals(signature)) {
+ return true;
+ }
+ }
+ } else {
+ // no signing history, just check the current signer
+ if (signingDetails.signatures.length == 1
+ && signingDetails.signatures[0].equals(signature)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Checks the signing certificates to see if the provided certificate is a member. Invalid for
+ * {@code SigningDetails} with multiple signing certificaes.
+ * @param sha256Certificate certificate to check for membership
+ * @param signingDetails signing certificates record whose members are to be searched
+ * @return true if {@code certificate} is in {@code signingDetails}
+ */
+ public static boolean signingDetailsHasSha256Certificate(
+ byte[] sha256Certificate, PackageParser.SigningDetails signingDetails ) {
+ if (signingDetails == PackageParser.SigningDetails.UNKNOWN) {
+ return false;
+ }
+ if (signingDetails.hasPastSigningCertificates()) {
+ for (int i = 0; i < signingDetails.pastSigningCertificates.length; i++) {
+ byte[] digest = PackageUtils.computeSha256DigestBytes(
+ signingDetails.pastSigningCertificates[i].toByteArray());
+ if (Arrays.equals(sha256Certificate, digest)) {
+ return true;
+ }
+ }
+ } else {
+ // no signing history, just check the current signer
+ if (signingDetails.signatures.length == 1) {
+ byte[] digest = PackageUtils.computeSha256DigestBytes(
+ signingDetails.signatures[0].toByteArray());
+ if (Arrays.equals(sha256Certificate, digest)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
/** Returns true to force apk verification if the updated package (in /data) is a priv app. */
static boolean isApkVerificationForced(@Nullable PackageSetting disabledPs) {
return disabledPs != null && disabledPs.isPrivileged() &&
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index c58c208..d67f2d7 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -23,6 +23,7 @@
import android.content.ComponentName;
import android.content.Context;
import android.graphics.Rect;
+import android.hardware.fingerprint.IFingerprintDialogReceiver;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
@@ -514,6 +515,56 @@
}
@Override
+ public void showFingerprintDialog(Bundle bundle, IFingerprintDialogReceiver receiver) {
+ if (mBar != null) {
+ try {
+ mBar.showFingerprintDialog(bundle, receiver);
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+
+ @Override
+ public void onFingerprintAuthenticated() {
+ if (mBar != null) {
+ try {
+ mBar.onFingerprintAuthenticated();
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+
+ @Override
+ public void onFingerprintHelp(String message) {
+ if (mBar != null) {
+ try {
+ mBar.onFingerprintHelp(message);
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+
+ @Override
+ public void onFingerprintError(String error) {
+ if (mBar != null) {
+ try {
+ mBar.onFingerprintError(error);
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+
+ @Override
+ public void hideFingerprintDialog() {
+ if (mBar != null) {
+ try {
+ mBar.hideFingerprintDialog();
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+
+ @Override
public void disable(int what, IBinder token, String pkg) {
disableForUser(what, token, pkg, mCurrentUserId);
}
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 0bc58e0..7540e26 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -104,6 +104,8 @@
"libhwbinder",
"libutils",
"libhwui",
+ "libbpf",
+ "libnetdutils",
"android.hardware.audio.common@2.0",
"android.hardware.broadcastradio@1.0",
"android.hardware.broadcastradio@1.1",
diff --git a/services/core/jni/com_android_server_net_NetworkStatsService.cpp b/services/core/jni/com_android_server_net_NetworkStatsService.cpp
index 8de24e5..3302dea 100644
--- a/services/core/jni/com_android_server_net_NetworkStatsService.cpp
+++ b/services/core/jni/com_android_server_net_NetworkStatsService.cpp
@@ -29,6 +29,15 @@
#include <utils/misc.h>
#include <utils/Log.h>
+#include "android-base/unique_fd.h"
+#include "bpf/BpfNetworkStats.h"
+#include "bpf/BpfUtils.h"
+
+using android::bpf::Stats;
+using android::bpf::hasBpfSupport;
+using android::bpf::bpfGetUidStats;
+using android::bpf::bpfGetIfaceStats;
+
namespace android {
static const char* QTAGUID_IFACE_STATS = "/proc/net/xt_qtaguid/iface_stat_fmt";
@@ -46,15 +55,6 @@
TCP_TX_PACKETS = 5
};
-struct Stats {
- uint64_t rxBytes;
- uint64_t rxPackets;
- uint64_t txBytes;
- uint64_t txPackets;
- uint64_t tcpRxPackets;
- uint64_t tcpTxPackets;
-};
-
static uint64_t getStatsType(struct Stats* stats, StatsType type) {
switch (type) {
case RX_BYTES:
@@ -150,9 +150,18 @@
return 0;
}
-static jlong getTotalStat(JNIEnv* env, jclass clazz, jint type) {
+static jlong getTotalStat(JNIEnv* env, jclass clazz, jint type, jboolean useBpfStats) {
struct Stats stats;
memset(&stats, 0, sizeof(Stats));
+
+ if (useBpfStats) {
+ if (bpfGetIfaceStats(NULL, &stats) == 0) {
+ return getStatsType(&stats, (StatsType) type);
+ } else {
+ return UNKNOWN;
+ }
+ }
+
if (parseIfaceStats(NULL, &stats) == 0) {
return getStatsType(&stats, (StatsType) type);
} else {
@@ -160,7 +169,8 @@
}
}
-static jlong getIfaceStat(JNIEnv* env, jclass clazz, jstring iface, jint type) {
+static jlong getIfaceStat(JNIEnv* env, jclass clazz, jstring iface, jint type,
+ jboolean useBpfStats) {
ScopedUtfChars iface8(env, iface);
if (iface8.c_str() == NULL) {
return UNKNOWN;
@@ -168,6 +178,15 @@
struct Stats stats;
memset(&stats, 0, sizeof(Stats));
+
+ if (useBpfStats) {
+ if (bpfGetIfaceStats(iface8.c_str(), &stats) == 0) {
+ return getStatsType(&stats, (StatsType) type);
+ } else {
+ return UNKNOWN;
+ }
+ }
+
if (parseIfaceStats(iface8.c_str(), &stats) == 0) {
return getStatsType(&stats, (StatsType) type);
} else {
@@ -175,9 +194,18 @@
}
}
-static jlong getUidStat(JNIEnv* env, jclass clazz, jint uid, jint type) {
+static jlong getUidStat(JNIEnv* env, jclass clazz, jint uid, jint type, jboolean useBpfStats) {
struct Stats stats;
memset(&stats, 0, sizeof(Stats));
+
+ if (useBpfStats) {
+ if (bpfGetUidStats(uid, &stats) == 0) {
+ return getStatsType(&stats, (StatsType) type);
+ } else {
+ return UNKNOWN;
+ }
+ }
+
if (parseUidStats(uid, &stats) == 0) {
return getStatsType(&stats, (StatsType) type);
} else {
@@ -186,9 +214,9 @@
}
static const JNINativeMethod gMethods[] = {
- {"nativeGetTotalStat", "(I)J", (void*) getTotalStat},
- {"nativeGetIfaceStat", "(Ljava/lang/String;I)J", (void*) getIfaceStat},
- {"nativeGetUidStat", "(II)J", (void*) getUidStat},
+ {"nativeGetTotalStat", "(IZ)J", (void*) getTotalStat},
+ {"nativeGetIfaceStat", "(Ljava/lang/String;IZ)J", (void*) getIfaceStat},
+ {"nativeGetUidStat", "(IIZ)J", (void*) getUidStat},
};
int register_android_server_net_NetworkStatsService(JNIEnv* env) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index 4f2866b..886747c 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -22,11 +22,13 @@
import android.os.UserHandle;
import android.security.keymaster.KeymasterCertificateChain;
import android.security.keystore.ParcelableKeyGenParameterSpec;
+import android.telephony.data.ApnSetting;
import com.android.internal.R;
import com.android.server.SystemService;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
/**
@@ -146,4 +148,32 @@
public List<String> getMeteredDataDisabled(ComponentName admin) {
return new ArrayList<>();
}
+
+ @Override
+ public int addOverrideApn(ComponentName admin, ApnSetting apnSetting) {
+ return -1;
+ }
+
+ @Override
+ public boolean updateOverrideApn(ComponentName admin, int apnId, ApnSetting apnSetting) {
+ return false;
+ }
+
+ @Override
+ public boolean removeOverrideApn(ComponentName admin, int apnId) {
+ return false;
+ }
+
+ @Override
+ public List<ApnSetting> getOverrideApns(ComponentName admin) {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public void setOverrideApnsEnabled(ComponentName admin, boolean enabled) {}
+
+ @Override
+ public boolean isOverrideApnEnabled(ComponentName admin) {
+ return false;
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 4d6989b..cb346cc 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -58,8 +58,15 @@
import static android.app.admin.DevicePolicyManager.WIPE_EXTERNAL_STORAGE;
import static android.app.admin.DevicePolicyManager.WIPE_RESET_PROTECTION_DATA;
import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_ENTRY_POINT_ADB;
-import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
+import static android.provider.Telephony.Carriers.DPC_URI;
+import static android.provider.Telephony.Carriers.ENFORCE_KEY;
+import static android.provider.Telephony.Carriers.ENFORCE_MANAGED_URI;
+
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent
+ .PROVISIONING_ENTRY_POINT_ADB;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker
+ .STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
+
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.END_TAG;
import static org.xmlpull.v1.XmlPullParser.TEXT;
@@ -99,6 +106,7 @@
import android.app.usage.UsageStatsManagerInternal;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
+import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -108,8 +116,8 @@
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
import android.content.pm.PermissionInfo;
import android.content.pm.ResolveInfo;
@@ -118,6 +126,7 @@
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.database.ContentObserver;
+import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.media.AudioManager;
@@ -161,13 +170,14 @@
import android.security.IKeyChainService;
import android.security.KeyChain;
import android.security.KeyChain.KeyChainConnection;
+import android.security.KeyStore;
import android.security.keymaster.KeymasterCertificateChain;
+import android.security.keystore.AttestationUtils;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.ParcelableKeyGenParameterSpec;
-import android.security.KeyStore;
-import android.security.keystore.AttestationUtils;
import android.service.persistentdata.PersistentDataBlockManager;
import android.telephony.TelephonyManager;
+import android.telephony.data.ApnSetting;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -217,7 +227,6 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
-import java.lang.IllegalStateException;
import java.lang.reflect.Constructor;
import java.nio.charset.StandardCharsets;
import java.text.DateFormat;
@@ -228,8 +237,8 @@
import java.util.Date;
import java.util.HashMap;
import java.util.List;
-import java.util.Map.Entry;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@@ -7283,6 +7292,15 @@
}
}
+ private void clearOverrideApnUnchecked() {
+ // Disable Override APNs and remove them from database.
+ setOverrideApnsEnabledUnchecked(false);
+ final List<ApnSetting> apns = getOverrideApnsUnchecked();
+ for (int i = 0; i < apns.size(); i ++) {
+ removeOverrideApnUnchecked(apns.get(i).getId());
+ }
+ }
+
private void clearDeviceOwnerLocked(ActiveAdmin admin, int userId) {
mDeviceAdminServiceController.stopServiceForOwner(userId, "clear-device-owner");
@@ -7303,6 +7321,7 @@
systemPolicyData.mLastNetworkLogsRetrievalTime = -1;
saveSettingsLocked(UserHandle.USER_SYSTEM);
clearUserPoliciesLocked(userId);
+ clearOverrideApnUnchecked();
mOwners.clearDeviceOwner();
mOwners.writeDeviceOwner();
@@ -12509,4 +12528,185 @@
.getResources().getString(R.string.printing_disabled_by, appLabel);
}
}
+
+ @Override
+ public int addOverrideApn(@NonNull ComponentName who, @NonNull ApnSetting apnSetting) {
+ if (!mHasFeature) {
+ return -1;
+ }
+ Preconditions.checkNotNull(who, "ComponentName is null in addOverrideApn");
+ Preconditions.checkNotNull(apnSetting, "ApnSetting is null in addOverrideApn");
+ synchronized (this) {
+ getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ }
+
+ int operatedId = -1;
+ Uri resultUri;
+ final long id = mInjector.binderClearCallingIdentity();
+ try {
+ resultUri = mContext.getContentResolver().insert(DPC_URI, apnSetting.toContentValues());
+ } finally {
+ mInjector.binderRestoreCallingIdentity(id);
+ }
+ if (resultUri != null) {
+ try {
+ operatedId = Integer.parseInt(resultUri.getLastPathSegment());
+ } catch (NumberFormatException e) {
+ Slog.e(LOG_TAG, "Failed to parse inserted override APN id.", e);
+ }
+ }
+
+ return operatedId;
+ }
+
+ @Override
+ public boolean updateOverrideApn(@NonNull ComponentName who, int apnId,
+ @NonNull ApnSetting apnSetting) {
+ if (!mHasFeature) {
+ return false;
+ }
+ Preconditions.checkNotNull(who, "ComponentName is null in updateOverrideApn");
+ Preconditions.checkNotNull(apnSetting, "ApnSetting is null in updateOverrideApn");
+ synchronized (this) {
+ getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ }
+
+ if (apnId < 0) {
+ return false;
+ }
+ final long id = mInjector.binderClearCallingIdentity();
+ try {
+ return mContext.getContentResolver().update(
+ Uri.withAppendedPath(DPC_URI, Integer.toString(apnId)),
+ apnSetting.toContentValues(), null, null) > 0;
+ } finally {
+ mInjector.binderRestoreCallingIdentity(id);
+ }
+ }
+
+ @Override
+ public boolean removeOverrideApn(@NonNull ComponentName who, int apnId) {
+ if (!mHasFeature) {
+ return false;
+ }
+ Preconditions.checkNotNull(who, "ComponentName is null in removeOverrideApn");
+ synchronized (this) {
+ getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ }
+
+ return removeOverrideApnUnchecked(apnId);
+ }
+
+ private boolean removeOverrideApnUnchecked(int apnId) {
+ if(apnId < 0) {
+ return false;
+ }
+ int numDeleted = 0;
+ final long id = mInjector.binderClearCallingIdentity();
+ try {
+ numDeleted = mContext.getContentResolver().delete(
+ Uri.withAppendedPath(DPC_URI, Integer.toString(apnId)), null, null);
+ } finally {
+ mInjector.binderRestoreCallingIdentity(id);
+ }
+ return numDeleted > 0;
+ }
+
+ @Override
+ public List<ApnSetting> getOverrideApns(@NonNull ComponentName who) {
+ if (!mHasFeature) {
+ return Collections.emptyList();
+ }
+ Preconditions.checkNotNull(who, "ComponentName is null in getOverrideApns");
+ synchronized (this) {
+ getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ }
+
+ return getOverrideApnsUnchecked();
+ }
+
+ private List<ApnSetting> getOverrideApnsUnchecked() {
+ final Cursor cursor;
+ final long id = mInjector.binderClearCallingIdentity();
+ try {
+ cursor = mContext.getContentResolver().query(DPC_URI, null, null, null, null);
+ } finally {
+ mInjector.binderRestoreCallingIdentity(id);
+ }
+
+ if (cursor == null) {
+ return Collections.emptyList();
+ }
+ try {
+ List<ApnSetting> apnList = new ArrayList<ApnSetting>();
+ cursor.moveToPosition(-1);
+ while (cursor.moveToNext()) {
+ ApnSetting apn = ApnSetting.makeApnSetting(cursor);
+ apnList.add(apn);
+ }
+ return apnList;
+ } finally {
+ cursor.close();
+ }
+ }
+
+ @Override
+ public void setOverrideApnsEnabled(@NonNull ComponentName who, boolean enabled) {
+ if (!mHasFeature) {
+ return;
+ }
+ Preconditions.checkNotNull(who, "ComponentName is null in setOverrideApnEnabled");
+ synchronized (this) {
+ getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ }
+
+ setOverrideApnsEnabledUnchecked(enabled);
+ }
+
+ private void setOverrideApnsEnabledUnchecked(boolean enabled) {
+ ContentValues value = new ContentValues();
+ value.put(ENFORCE_KEY, enabled);
+ final long id = mInjector.binderClearCallingIdentity();
+ try {
+ mContext.getContentResolver().update(
+ ENFORCE_MANAGED_URI, value, null, null);
+ } finally {
+ mInjector.binderRestoreCallingIdentity(id);
+ }
+ }
+
+ @Override
+ public boolean isOverrideApnEnabled(@NonNull ComponentName who) {
+ if (!mHasFeature) {
+ return false;
+ }
+ Preconditions.checkNotNull(who, "ComponentName is null in isOverrideApnEnabled");
+ synchronized (this) {
+ getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ }
+
+ Cursor enforceCursor;
+ final long id = mInjector.binderClearCallingIdentity();
+ try {
+ enforceCursor = mContext.getContentResolver().query(
+ ENFORCE_MANAGED_URI, null, null, null, null);
+ } finally {
+ mInjector.binderRestoreCallingIdentity(id);
+ }
+
+ if (enforceCursor == null) {
+ return false;
+ }
+ try {
+ if (enforceCursor.moveToFirst()) {
+ return enforceCursor.getInt(enforceCursor.getColumnIndex(ENFORCE_KEY)) == 1;
+ }
+ } catch (IllegalArgumentException e) {
+ Slog.e(LOG_TAG, "Cursor returned from ENFORCE_MANAGED_URI doesn't contain "
+ + "correct info.", e);
+ } finally {
+ enforceCursor.close();
+ }
+ return false;
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java b/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java
index de54e52..a29e169 100644
--- a/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java
@@ -15,6 +15,8 @@
*/
package com.android.server;
+import static android.app.ActivityManager.PROCESS_STATE_CACHED_EMPTY;
+
import static com.android.server.ForceAppStandbyTracker.TARGET_OP;
import static org.junit.Assert.assertEquals;
@@ -33,6 +35,7 @@
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
import android.app.AppOpsManager;
import android.app.AppOpsManager.OpEntry;
import android.app.AppOpsManager.PackageOps;
@@ -259,13 +262,19 @@
private static final int JOBS_AND_ALARMS = ALARMS_ONLY | JOBS_ONLY;
private void areRestricted(ForceAppStandbyTrackerTestable instance, int uid, String packageName,
- int restrictionTypes) {
+ int restrictionTypes, boolean exemptFromBatterySaver) {
assertEquals(((restrictionTypes & JOBS_ONLY) != 0),
- instance.areJobsRestricted(uid, packageName));
+ instance.areJobsRestricted(uid, packageName, exemptFromBatterySaver));
assertEquals(((restrictionTypes & ALARMS_ONLY) != 0),
instance.areAlarmsRestricted(uid, packageName));
}
+ private void areRestricted(ForceAppStandbyTrackerTestable instance, int uid, String packageName,
+ int restrictionTypes) {
+ areRestricted(instance, uid, packageName, restrictionTypes,
+ /*exemptFromBatterySaver=*/ false);
+ }
+
@Test
public void testAll() throws Exception {
final ForceAppStandbyTrackerTestable instance = newInstance();
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java
index 8d5556e..07262e1 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java
@@ -23,6 +23,8 @@
import static com.android.server.testutils.TestUtils.strictMock;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
@@ -32,6 +34,7 @@
import android.annotation.NonNull;
import android.content.Context;
+import android.os.Handler;
import android.os.Message;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
@@ -46,7 +49,9 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.concurrent.CompletableFuture;
import java.util.function.IntConsumer;
+import java.util.function.Supplier;
/**
@@ -130,7 +135,7 @@
}
@NonNull
- public MagnificationGestureHandler newInstance(boolean detectTripleTap,
+ private MagnificationGestureHandler newInstance(boolean detectTripleTap,
boolean detectShortcutTrigger) {
MagnificationGestureHandler h = new MagnificationGestureHandler(
mContext, mMagnificationController,
@@ -192,6 +197,16 @@
});
}
+ @Test
+ public void testTransitionToDelegatingStateAndClear_preservesShortcutTriggeredState() {
+ mMgh.mDetectingState.transitionToDelegatingStateAndClear();
+ assertFalse(mMgh.mDetectingState.mShortcutTriggered);
+
+ goFromStateIdleTo(STATE_SHORTCUT_TRIGGERED);
+ mMgh.mDetectingState.transitionToDelegatingStateAndClear();
+ assertTrue(mMgh.mDetectingState.mShortcutTriggered);
+ }
+
/**
* Covers edges of the graph not covered by "canonical" transitions specified in
* {@link #goFromStateIdleTo} and {@link #returnToNormalFrom}
@@ -510,14 +525,20 @@
fastForward(1);
}
+ private static MotionEvent fromTouchscreen(MotionEvent ev) {
+ ev.setSource(InputDevice.SOURCE_TOUCHSCREEN);
+ return ev;
+ }
+
private MotionEvent moveEvent(float x, float y) {
- return MotionEvent.obtain(mLastDownTime, mClock.now(), ACTION_MOVE, x, y, 0);
+ return fromTouchscreen(
+ MotionEvent.obtain(mLastDownTime, mClock.now(), ACTION_MOVE, x, y, 0));
}
private MotionEvent downEvent() {
mLastDownTime = mClock.now();
- return MotionEvent.obtain(mLastDownTime, mLastDownTime,
- ACTION_DOWN, DEFAULT_X, DEFAULT_Y, 0);
+ return fromTouchscreen(MotionEvent.obtain(mLastDownTime, mLastDownTime,
+ ACTION_DOWN, DEFAULT_X, DEFAULT_Y, 0));
}
private MotionEvent upEvent() {
@@ -525,8 +546,8 @@
}
private MotionEvent upEvent(long downTime) {
- return MotionEvent.obtain(downTime, mClock.now(),
- MotionEvent.ACTION_UP, DEFAULT_X, DEFAULT_Y, 0);
+ return fromTouchscreen(MotionEvent.obtain(downTime, mClock.now(),
+ MotionEvent.ACTION_UP, DEFAULT_X, DEFAULT_Y, 0));
}
private MotionEvent pointerEvent(int action, float x, float y) {
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index e8d6ed2..5825a8b7 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -78,6 +78,7 @@
import android.security.KeyChain;
import android.security.keystore.AttestationUtils;
import android.telephony.TelephonyManager;
+import android.telephony.data.ApnSetting;
import android.test.MoreAsserts;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.ArraySet;
@@ -4610,6 +4611,23 @@
MockUtils.checkUserHandle(UserHandle.USER_SYSTEM));
}
+ public void testOverrideApnAPIsFailWithPO() throws Exception {
+ setupProfileOwner();
+ ApnSetting apn = (new ApnSetting.Builder()).build();
+ assertExpectException(SecurityException.class, null, () ->
+ dpm.addOverrideApn(admin1, apn));
+ assertExpectException(SecurityException.class, null, () ->
+ dpm.updateOverrideApn(admin1, 0, apn));
+ assertExpectException(SecurityException.class, null, () ->
+ dpm.removeOverrideApn(admin1, 0));
+ assertExpectException(SecurityException.class, null, () ->
+ dpm.getOverrideApns(admin1));
+ assertExpectException(SecurityException.class, null, () ->
+ dpm.setOverrideApnsEnabled(admin1, false));
+ assertExpectException(SecurityException.class, null, () ->
+ dpm.isOverrideApnEnabled(admin1));
+ }
+
private void verifyCanGetOwnerInstalledCaCerts(
final ComponentName caller, final DpmMockContext callerContext) throws Exception {
final String alias = "cert";
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
index 0343a52..34c69f5 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
@@ -32,6 +32,7 @@
import android.app.backup.IBackupManager;
import android.app.usage.UsageStatsManagerInternal;
import android.content.BroadcastReceiver;
+import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -39,8 +40,10 @@
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.UserInfo;
+import android.database.Cursor;
import android.media.IAudioService;
import android.net.IIpConnectivityMetrics;
+import android.net.Uri;
import android.net.wifi.WifiManager;
import android.os.Handler;
import android.os.PowerManager;
@@ -51,6 +54,7 @@
import android.provider.Settings;
import android.security.KeyChain;
import android.telephony.TelephonyManager;
+import android.test.mock.MockContentProvider;
import android.test.mock.MockContentResolver;
import android.util.ArrayMap;
import android.util.Pair;
@@ -144,6 +148,23 @@
packageManager = spy(realContext.getPackageManager());
contentResolver = new MockContentResolver();
+ contentResolver.addProvider("telephony", new MockContentProvider(realContext) {
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ return 0;
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+ return null;
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ return 0;
+ }
+ });
contentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
// Add the system user with a fake profile group already set up (this can happen in the real
diff --git a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
index 43d026d..e2064aa 100644
--- a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
@@ -47,6 +47,8 @@
/**
* Test reading and writing correctly from file.
+ *
+ * atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
*/
@RunWith(AndroidJUnit4.class)
public class JobStoreTest {
@@ -116,6 +118,7 @@
.setPersisted(true)
.build();
final JobStatus ts = JobStatus.createFromJobInfo(task, SOME_UID, null, -1, null);
+ ts.addInternalFlags(JobStatus.INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION);
mTaskStoreUnderTest.add(ts);
waitForPendingIo();
@@ -128,6 +131,8 @@
assertTasksEqual(task, loadedTaskStatus.getJob());
assertTrue("JobStore#contains invalid.", mTaskStoreUnderTest.containsJob(ts));
assertEquals("Different uids.", SOME_UID, loadedTaskStatus.getUid());
+ assertEquals(JobStatus.INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION,
+ loadedTaskStatus.getInternalFlags());
compareTimestampsSubjectToIoLatency("Early run-times not the same after read.",
ts.getEarliestRunTime(), loadedTaskStatus.getEarliestRunTime());
compareTimestampsSubjectToIoLatency("Late run-times not the same after read.",
@@ -272,7 +277,7 @@
0 /* sourceUserId */, 0, 0, "someTag",
invalidEarlyRuntimeElapsedMillis, invalidLateRuntimeElapsedMillis,
0 /* lastSuccessfulRunTime */, 0 /* lastFailedRunTime */,
- persistedExecutionTimesUTC);
+ persistedExecutionTimesUTC, 0 /* innerFlagg */);
mTaskStoreUnderTest.add(js);
waitForPendingIo();
diff --git a/services/tests/servicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java b/services/tests/servicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
index f6a749d..35cba18 100644
--- a/services/tests/servicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
@@ -164,6 +164,6 @@
private static JobStatus createJobStatus(JobInfo.Builder job, long earliestRunTimeElapsedMillis,
long latestRunTimeElapsedMillis) {
return new JobStatus(job.build(), 0, null, -1, 0, 0, null, earliestRunTimeElapsedMillis,
- latestRunTimeElapsedMillis, 0, 0, null);
+ latestRunTimeElapsedMillis, 0, 0, null, 0);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/job/controllers/JobStatusTest.java b/services/tests/servicestests/src/com/android/server/job/controllers/JobStatusTest.java
index 15c24ac..d78af22 100644
--- a/services/tests/servicestests/src/com/android/server/job/controllers/JobStatusTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/controllers/JobStatusTest.java
@@ -71,6 +71,6 @@
final JobInfo job = new JobInfo.Builder(101, new ComponentName("foo", "bar"))
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY).build();
return new JobStatus(job, 0, null, -1, 0, 0, null, earliestRunTimeElapsedMillis,
- latestRunTimeElapsedMillis, 0, 0, null);
+ latestRunTimeElapsedMillis, 0, 0, null, 0);
}
}
diff --git a/services/usage/java/com/android/server/usage/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java
index cb32d1f..4d458b0 100644
--- a/services/usage/java/com/android/server/usage/IntervalStats.java
+++ b/services/usage/java/com/android/server/usage/IntervalStats.java
@@ -92,6 +92,17 @@
return false;
}
+ /**
+ * Returns whether the event type is one caused by user visible
+ * interaction. Excludes those that are internally generated.
+ * @param eventType
+ * @return
+ */
+ private boolean isUserVisibleEvent(int eventType) {
+ return eventType != UsageEvents.Event.SYSTEM_INTERACTION
+ && eventType != UsageEvents.Event.STANDBY_BUCKET_CHANGED;
+ }
+
void update(String packageName, long timeStamp, int eventType) {
UsageStats usageStats = getOrCreateUsageStats(packageName);
@@ -109,7 +120,7 @@
usageStats.mLastEvent = eventType;
}
- if (eventType != UsageEvents.Event.SYSTEM_INTERACTION) {
+ if (isUserVisibleEvent(eventType)) {
usageStats.mLastTimeUsed = timeStamp;
}
usageStats.mEndTimeStamp = timeStamp;
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 979feaa..096fdcc 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -115,6 +115,26 @@
AppStandbyController mAppStandby;
+ private UsageStatsManagerInternal.AppIdleStateChangeListener mStandbyChangeListener =
+ new UsageStatsManagerInternal.AppIdleStateChangeListener() {
+ @Override
+ public void onAppIdleStateChanged(String packageName, int userId, boolean idle,
+ int bucket) {
+ Event event = new Event();
+ event.mEventType = Event.STANDBY_BUCKET_CHANGED;
+ event.mBucket = bucket;
+ event.mPackage = packageName;
+ // This will later be converted to system time.
+ event.mTimeStamp = SystemClock.elapsedRealtime();
+ mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
+ }
+
+ @Override
+ public void onParoleStateChanged(boolean isParoleOn) {
+
+ }
+ };
+
public UsageStatsService(Context context) {
super(context);
}
@@ -129,6 +149,7 @@
mAppStandby = new AppStandbyController(getContext(), BackgroundThread.get().getLooper());
+ mAppStandby.addListener(mStandbyChangeListener);
File systemDataDir = new File(Environment.getDataDirectory(), "system");
mUsageStatsDir = new File(systemDataDir, "usagestats");
mUsageStatsDir.mkdirs();
diff --git a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
index cc53a9c..d1ed599 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
@@ -64,6 +64,7 @@
private static final String LAST_EVENT_ATTR = "lastEvent";
private static final String TYPE_ATTR = "type";
private static final String SHORTCUT_ID_ATTR = "shortcutId";
+ private static final String STANDBY_BUCKET_ATTR = "standbyBucket";
// Time attributes stored as an offset of the beginTime.
private static final String LAST_TIME_ACTIVE_ATTR = "lastTimeActive";
@@ -173,6 +174,9 @@
final String id = XmlUtils.readStringAttribute(parser, SHORTCUT_ID_ATTR);
event.mShortcutId = (id != null) ? id.intern() : null;
break;
+ case UsageEvents.Event.STANDBY_BUCKET_CHANGED:
+ event.mBucket = XmlUtils.readIntAttribute(parser, STANDBY_BUCKET_ATTR, 0);
+ break;
}
if (statsOut.events == null) {
@@ -276,6 +280,10 @@
XmlUtils.writeStringAttribute(xml, SHORTCUT_ID_ATTR, event.mShortcutId);
}
break;
+ case UsageEvents.Event.STANDBY_BUCKET_CHANGED:
+ if (event.mBucket != 0) {
+ XmlUtils.writeIntAttribute(xml, STANDBY_BUCKET_ATTR, event.mBucket);
+ }
}
xml.endTag(null, EVENT_TAG);
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index f02221c..ec12da2 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -599,6 +599,9 @@
if (event.mShortcutId != null) {
pw.printPair("shortcutId", event.mShortcutId);
}
+ if (event.mEventType == UsageEvents.Event.STANDBY_BUCKET_CHANGED) {
+ pw.printPair("standbyBucket", event.mBucket);
+ }
pw.printHexPair("flags", event.mFlags);
pw.println();
}
@@ -645,6 +648,8 @@
return "CHOOSER_ACTION";
case UsageEvents.Event.NOTIFICATION_SEEN:
return "NOTIFICATION_SEEN";
+ case UsageEvents.Event.STANDBY_BUCKET_CHANGED:
+ return "STANDBY_BUCKET_CHANGED";
default:
return "UNKNOWN";
}
diff --git a/telephony/java/android/telephony/euicc/EuiccCardManager.java b/telephony/java/android/telephony/euicc/EuiccCardManager.java
index b9ed005..88bae33 100644
--- a/telephony/java/android/telephony/euicc/EuiccCardManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccCardManager.java
@@ -142,11 +142,12 @@
/**
* Gets all the profiles on eUicc.
*
+ * @param cardId The Id of the eUICC.
* @param callback The callback to get the result code and all the profiles.
*/
- public void getAllProfiles(ResultCallback<EuiccProfileInfo[]> callback) {
+ public void getAllProfiles(String cardId, ResultCallback<EuiccProfileInfo[]> callback) {
try {
- getIEuiccCardController().getAllProfiles(mContext.getOpPackageName(),
+ getIEuiccCardController().getAllProfiles(mContext.getOpPackageName(), cardId,
new IGetAllProfilesCallback.Stub() {
@Override
public void onComplete(int resultCode, EuiccProfileInfo[] profiles) {
@@ -162,12 +163,13 @@
/**
* Gets the profile of the given iccid.
*
+ * @param cardId The Id of the eUICC.
* @param iccid The iccid of the profile.
* @param callback The callback to get the result code and profile.
*/
- public void getProfile(String iccid, ResultCallback<EuiccProfileInfo> callback) {
+ public void getProfile(String cardId, String iccid, ResultCallback<EuiccProfileInfo> callback) {
try {
- getIEuiccCardController().getProfile(mContext.getOpPackageName(), iccid,
+ getIEuiccCardController().getProfile(mContext.getOpPackageName(), cardId, iccid,
new IGetProfileCallback.Stub() {
@Override
public void onComplete(int resultCode, EuiccProfileInfo profile) {
@@ -183,14 +185,16 @@
/**
* Disables the profile of the given iccid.
*
+ * @param cardId The Id of the eUICC.
* @param iccid The iccid of the profile.
* @param refresh Whether sending the REFRESH command to modem.
* @param callback The callback to get the result code.
*/
- public void disableProfile(String iccid, boolean refresh, ResultCallback<Void> callback) {
+ public void disableProfile(String cardId, String iccid, boolean refresh,
+ ResultCallback<Void> callback) {
try {
- getIEuiccCardController().disableProfile(mContext.getOpPackageName(), iccid, refresh,
- new IDisableProfileCallback.Stub() {
+ getIEuiccCardController().disableProfile(mContext.getOpPackageName(), cardId, iccid,
+ refresh, new IDisableProfileCallback.Stub() {
@Override
public void onComplete(int resultCode) {
callback.onComplete(resultCode, null);
@@ -206,15 +210,16 @@
* Switches from the current profile to another profile. The current profile will be disabled
* and the specified profile will be enabled.
*
+ * @param cardId The Id of the eUICC.
* @param iccid The iccid of the profile to switch to.
* @param refresh Whether sending the REFRESH command to modem.
* @param callback The callback to get the result code and the EuiccProfileInfo enabled.
*/
- public void switchToProfile(String iccid, boolean refresh,
+ public void switchToProfile(String cardId, String iccid, boolean refresh,
ResultCallback<EuiccProfileInfo> callback) {
try {
- getIEuiccCardController().switchToProfile(mContext.getOpPackageName(), iccid, refresh,
- new ISwitchToProfileCallback.Stub() {
+ getIEuiccCardController().switchToProfile(mContext.getOpPackageName(), cardId, iccid,
+ refresh, new ISwitchToProfileCallback.Stub() {
@Override
public void onComplete(int resultCode, EuiccProfileInfo profile) {
callback.onComplete(resultCode, profile);
@@ -227,30 +232,18 @@
}
/**
- * Gets the EID of the eUICC.
- *
- * @return The EID.
- */
- public String getEid() {
- try {
- return getIEuiccCardController().getEid();
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling getEid", e);
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
* Sets the nickname of the profile of the given iccid.
*
+ * @param cardId The Id of the eUICC.
* @param iccid The iccid of the profile.
* @param nickname The nickname of the profile.
* @param callback The callback to get the result code.
*/
- public void setNickname(String iccid, String nickname, ResultCallback<Void> callback) {
+ public void setNickname(String cardId, String iccid, String nickname,
+ ResultCallback<Void> callback) {
try {
- getIEuiccCardController().setNickname(mContext.getOpPackageName(), iccid, nickname,
- new ISetNicknameCallback.Stub() {
+ getIEuiccCardController().setNickname(mContext.getOpPackageName(), cardId, iccid,
+ nickname, new ISetNicknameCallback.Stub() {
@Override
public void onComplete(int resultCode) {
callback.onComplete(resultCode, null);
@@ -265,12 +258,13 @@
/**
* Deletes the profile of the given iccid from eUICC.
*
+ * @param cardId The Id of the eUICC.
* @param iccid The iccid of the profile.
* @param callback The callback to get the result code.
*/
- public void deleteProfile(String iccid, ResultCallback<Void> callback) {
+ public void deleteProfile(String cardId, String iccid, ResultCallback<Void> callback) {
try {
- getIEuiccCardController().deleteProfile(mContext.getOpPackageName(), iccid,
+ getIEuiccCardController().deleteProfile(mContext.getOpPackageName(), cardId, iccid,
new IDeleteProfileCallback.Stub() {
@Override
public void onComplete(int resultCode) {
@@ -286,13 +280,14 @@
/**
* Resets the eUICC memory.
*
+ * @param cardId The Id of the eUICC.
* @param options Bits of the options of resetting which parts of the eUICC memory. See
* EuiccCard for details.
* @param callback The callback to get the result code.
*/
- public void resetMemory(@ResetOption int options, ResultCallback<Void> callback) {
+ public void resetMemory(String cardId, @ResetOption int options, ResultCallback<Void> callback) {
try {
- getIEuiccCardController().resetMemory(mContext.getOpPackageName(), options,
+ getIEuiccCardController().resetMemory(mContext.getOpPackageName(), cardId, options,
new IResetMemoryCallback.Stub() {
@Override
public void onComplete(int resultCode) {
@@ -308,11 +303,12 @@
/**
* Gets the default SM-DP+ address from eUICC.
*
+ * @param cardId The Id of the eUICC.
* @param callback The callback to get the result code and the default SM-DP+ address.
*/
- public void getDefaultSmdpAddress(ResultCallback<String> callback) {
+ public void getDefaultSmdpAddress(String cardId, ResultCallback<String> callback) {
try {
- getIEuiccCardController().getDefaultSmdpAddress(mContext.getOpPackageName(),
+ getIEuiccCardController().getDefaultSmdpAddress(mContext.getOpPackageName(), cardId,
new IGetDefaultSmdpAddressCallback.Stub() {
@Override
public void onComplete(int resultCode, String address) {
@@ -328,11 +324,12 @@
/**
* Gets the SM-DS address from eUICC.
*
+ * @param cardId The Id of the eUICC.
* @param callback The callback to get the result code and the SM-DS address.
*/
- public void getSmdsAddress(ResultCallback<String> callback) {
+ public void getSmdsAddress(String cardId, ResultCallback<String> callback) {
try {
- getIEuiccCardController().getSmdsAddress(mContext.getOpPackageName(),
+ getIEuiccCardController().getSmdsAddress(mContext.getOpPackageName(), cardId,
new IGetSmdsAddressCallback.Stub() {
@Override
public void onComplete(int resultCode, String address) {
@@ -348,12 +345,13 @@
/**
* Sets the default SM-DP+ address of eUICC.
*
+ * @param cardId The Id of the eUICC.
* @param defaultSmdpAddress The default SM-DP+ address to set.
* @param callback The callback to get the result code.
*/
- public void setDefaultSmdpAddress(String defaultSmdpAddress, ResultCallback<Void> callback) {
+ public void setDefaultSmdpAddress(String cardId, String defaultSmdpAddress, ResultCallback<Void> callback) {
try {
- getIEuiccCardController().setDefaultSmdpAddress(mContext.getOpPackageName(),
+ getIEuiccCardController().setDefaultSmdpAddress(mContext.getOpPackageName(), cardId,
defaultSmdpAddress,
new ISetDefaultSmdpAddressCallback.Stub() {
@Override
@@ -370,11 +368,12 @@
/**
* Gets Rules Authorisation Table.
*
+ * @param cardId The Id of the eUICC.
* @param callback the callback to get the result code and the rule authorisation table.
*/
- public void getRulesAuthTable(ResultCallback<EuiccRulesAuthTable> callback) {
+ public void getRulesAuthTable(String cardId, ResultCallback<EuiccRulesAuthTable> callback) {
try {
- getIEuiccCardController().getRulesAuthTable(mContext.getOpPackageName(),
+ getIEuiccCardController().getRulesAuthTable(mContext.getOpPackageName(), cardId,
new IGetRulesAuthTableCallback.Stub() {
@Override
public void onComplete(int resultCode, EuiccRulesAuthTable rat) {
@@ -390,11 +389,12 @@
/**
* Gets the eUICC challenge for new profile downloading.
*
+ * @param cardId The Id of the eUICC.
* @param callback the callback to get the result code and the challenge.
*/
- public void getEuiccChallenge(ResultCallback<byte[]> callback) {
+ public void getEuiccChallenge(String cardId, ResultCallback<byte[]> callback) {
try {
- getIEuiccCardController().getEuiccChallenge(mContext.getOpPackageName(),
+ getIEuiccCardController().getEuiccChallenge(mContext.getOpPackageName(), cardId,
new IGetEuiccChallengeCallback.Stub() {
@Override
public void onComplete(int resultCode, byte[] challenge) {
@@ -410,11 +410,12 @@
/**
* Gets the eUICC info1 defined in GSMA RSP v2.0+ for new profile downloading.
*
+ * @param cardId The Id of the eUICC.
* @param callback the callback to get the result code and the info1.
*/
- public void getEuiccInfo1(ResultCallback<byte[]> callback) {
+ public void getEuiccInfo1(String cardId, ResultCallback<byte[]> callback) {
try {
- getIEuiccCardController().getEuiccInfo1(mContext.getOpPackageName(),
+ getIEuiccCardController().getEuiccInfo1(mContext.getOpPackageName(), cardId,
new IGetEuiccInfo1Callback.Stub() {
@Override
public void onComplete(int resultCode, byte[] info) {
@@ -430,11 +431,12 @@
/**
* Gets the eUICC info2 defined in GSMA RSP v2.0+ for new profile downloading.
*
+ * @param cardId The Id of the eUICC.
* @param callback the callback to get the result code and the info2.
*/
- public void getEuiccInfo2(ResultCallback<byte[]> callback) {
+ public void getEuiccInfo2(String cardId, ResultCallback<byte[]> callback) {
try {
- getIEuiccCardController().getEuiccInfo2(mContext.getOpPackageName(),
+ getIEuiccCardController().getEuiccInfo2(mContext.getOpPackageName(), cardId,
new IGetEuiccInfo2Callback.Stub() {
@Override
public void onComplete(int resultCode, byte[] info) {
@@ -450,6 +452,7 @@
/**
* Authenticates the SM-DP+ server by the eUICC.
*
+ * @param cardId The Id of the eUICC.
* @param matchingId the activation code token defined in GSMA RSP v2.0+ or empty when it is not
* required.
* @param serverSigned1 ASN.1 data in byte array signed and returned by the SM-DP+ server.
@@ -463,12 +466,13 @@
* @param callback the callback to get the result code and a byte array which represents a
* {@code AuthenticateServerResponse} defined in GSMA RSP v2.0+.
*/
- public void authenticateServer(String matchingId, byte[] serverSigned1,
+ public void authenticateServer(String cardId, String matchingId, byte[] serverSigned1,
byte[] serverSignature1, byte[] euiccCiPkIdToBeUsed, byte[] serverCertificate,
ResultCallback<byte[]> callback) {
try {
getIEuiccCardController().authenticateServer(
mContext.getOpPackageName(),
+ cardId,
matchingId,
serverSigned1,
serverSignature1,
@@ -489,6 +493,7 @@
/**
* Prepares the profile download request sent to SM-DP+.
*
+ * @param cardId The Id of the eUICC.
* @param hashCc the hash of confirmation code. It can be null if there is no confirmation code
* required.
* @param smdpSigned2 ASN.1 data in byte array indicating the data to be signed by the SM-DP+
@@ -500,11 +505,12 @@
* @param callback the callback to get the result code and a byte array which represents a
* {@code PrepareDownloadResponse} defined in GSMA RSP v2.0+
*/
- public void prepareDownload(@Nullable byte[] hashCc, byte[] smdpSigned2,
+ public void prepareDownload(String cardId, @Nullable byte[] hashCc, byte[] smdpSigned2,
byte[] smdpSignature2, byte[] smdpCertificate, ResultCallback<byte[]> callback) {
try {
getIEuiccCardController().prepareDownload(
mContext.getOpPackageName(),
+ cardId,
hashCc,
smdpSigned2,
smdpSignature2,
@@ -524,15 +530,17 @@
/**
* Loads a downloaded bound profile package onto the eUICC.
*
+ * @param cardId The Id of the eUICC.
* @param boundProfilePackage the Bound Profile Package data returned by SM-DP+ server.
* @param callback the callback to get the result code and a byte array which represents a
* {@code LoadBoundProfilePackageResponse} defined in GSMA RSP v2.0+.
*/
- public void loadBoundProfilePackage(byte[] boundProfilePackage,
+ public void loadBoundProfilePackage(String cardId, byte[] boundProfilePackage,
ResultCallback<byte[]> callback) {
try {
getIEuiccCardController().loadBoundProfilePackage(
mContext.getOpPackageName(),
+ cardId,
boundProfilePackage,
new ILoadBoundProfilePackageCallback.Stub() {
@Override
@@ -549,16 +557,18 @@
/**
* Cancels the current profile download session.
*
+ * @param cardId The Id of the eUICC.
* @param transactionId the transaction ID returned by SM-DP+ server.
* @param reason the cancel reason.
* @param callback the callback to get the result code and an byte[] which represents a
* {@code CancelSessionResponse} defined in GSMA RSP v2.0+.
*/
- public void cancelSession(byte[] transactionId, @CancelReason int reason,
+ public void cancelSession(String cardId, byte[] transactionId, @CancelReason int reason,
ResultCallback<byte[]> callback) {
try {
getIEuiccCardController().cancelSession(
mContext.getOpPackageName(),
+ cardId,
transactionId,
reason,
new ICancelSessionCallback.Stub() {
@@ -576,13 +586,14 @@
/**
* Lists all notifications of the given {@code notificationEvents}.
*
+ * @param cardId The Id of the eUICC.
* @param events bits of the event types ({@link EuiccNotification.Event}) to list.
* @param callback the callback to get the result code and the list of notifications.
*/
- public void listNotifications(@EuiccNotification.Event int events,
+ public void listNotifications(String cardId, @EuiccNotification.Event int events,
ResultCallback<EuiccNotification[]> callback) {
try {
- getIEuiccCardController().listNotifications(mContext.getOpPackageName(), events,
+ getIEuiccCardController().listNotifications(mContext.getOpPackageName(), cardId, events,
new IListNotificationsCallback.Stub() {
@Override
public void onComplete(int resultCode, EuiccNotification[] notifications) {
@@ -598,14 +609,15 @@
/**
* Retrieves contents of all notification of the given {@code events}.
*
+ * @param cardId The Id of the eUICC.
* @param events bits of the event types ({@link EuiccNotification.Event}) to list.
* @param callback the callback to get the result code and the list of notifications.
*/
- public void retrieveNotificationList(@EuiccNotification.Event int events,
+ public void retrieveNotificationList(String cardId, @EuiccNotification.Event int events,
ResultCallback<EuiccNotification[]> callback) {
try {
- getIEuiccCardController().retrieveNotificationList(mContext.getOpPackageName(), events,
- new IRetrieveNotificationListCallback.Stub() {
+ getIEuiccCardController().retrieveNotificationList(mContext.getOpPackageName(), cardId,
+ events, new IRetrieveNotificationListCallback.Stub() {
@Override
public void onComplete(int resultCode, EuiccNotification[] notifications) {
callback.onComplete(resultCode, notifications);
@@ -620,13 +632,15 @@
/**
* Retrieves the content of a notification of the given {@code seqNumber}.
*
+ * @param cardId The Id of the eUICC.
* @param seqNumber the sequence number of the notification.
* @param callback the callback to get the result code and the notification.
*/
- public void retrieveNotification(int seqNumber, ResultCallback<EuiccNotification> callback) {
+ public void retrieveNotification(String cardId, int seqNumber,
+ ResultCallback<EuiccNotification> callback) {
try {
- getIEuiccCardController().retrieveNotification(mContext.getOpPackageName(), seqNumber,
- new IRetrieveNotificationCallback.Stub() {
+ getIEuiccCardController().retrieveNotification(mContext.getOpPackageName(), cardId,
+ seqNumber, new IRetrieveNotificationCallback.Stub() {
@Override
public void onComplete(int resultCode, EuiccNotification notification) {
callback.onComplete(resultCode, notification);
@@ -641,13 +655,16 @@
/**
* Removes a notification from eUICC.
*
+ * @param cardId The Id of the eUICC.
* @param seqNumber the sequence number of the notification.
* @param callback the callback to get the result code.
*/
- public void removeNotificationFromList(int seqNumber, ResultCallback<Void> callback) {
+ public void removeNotificationFromList(String cardId, int seqNumber,
+ ResultCallback<Void> callback) {
try {
getIEuiccCardController().removeNotificationFromList(
mContext.getOpPackageName(),
+ cardId,
seqNumber,
new IRemoveNotificationFromListCallback.Stub() {
@Override
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index d146707..7f913ce 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -19,6 +19,7 @@
import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Context;
@@ -598,7 +599,11 @@
}
}
- private static IEuiccController getIEuiccController() {
+ /**
+ * @hide
+ */
+ @TestApi
+ protected IEuiccController getIEuiccController() {
return IEuiccController.Stub.asInterface(ServiceManager.getService("econtroller"));
}
}
diff --git a/telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl b/telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl
index abc55c7..e33f44c 100644
--- a/telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl
@@ -41,42 +41,49 @@
/** @hide */
interface IEuiccCardController {
- oneway void getAllProfiles(String callingPackage, in IGetAllProfilesCallback callback);
- oneway void getProfile(String callingPackage, String iccid, in IGetProfileCallback callback);
- oneway void disableProfile(String callingPackage, String iccid, boolean refresh,
+ oneway void getAllProfiles(String callingPackage, String cardId,
+ in IGetAllProfilesCallback callback);
+ oneway void getProfile(String callingPackage, String cardId, String iccid,
+ in IGetProfileCallback callback);
+ oneway void disableProfile(String callingPackage, String cardId, String iccid, boolean refresh,
in IDisableProfileCallback callback);
- oneway void switchToProfile(String callingPackage, String iccid, boolean refresh,
+ oneway void switchToProfile(String callingPackage, String cardId, String iccid, boolean refresh,
in ISwitchToProfileCallback callback);
- String getEid();
- oneway void setNickname(String callingPackage, String iccid, String nickname,
+ oneway void setNickname(String callingPackage, String cardId, String iccid, String nickname,
in ISetNicknameCallback callback);
- oneway void deleteProfile(String callingPackage, String iccid,
+ oneway void deleteProfile(String callingPackage, String cardId, String iccid,
in IDeleteProfileCallback callback);
- oneway void resetMemory(String callingPackage, int options, in IResetMemoryCallback callback);
- oneway void getDefaultSmdpAddress(String callingPackage,
+ oneway void resetMemory(String callingPackage, String cardId, int options, in IResetMemoryCallback callback);
+ oneway void getDefaultSmdpAddress(String callingPackage, String cardId,
in IGetDefaultSmdpAddressCallback callback);
- oneway void getSmdsAddress(String callingPackage, in IGetSmdsAddressCallback callback);
- oneway void setDefaultSmdpAddress(String callingPackage, String address,
+ oneway void getSmdsAddress(String callingPackage, String cardId,
+ in IGetSmdsAddressCallback callback);
+ oneway void setDefaultSmdpAddress(String callingPackage, String cardId, String address,
in ISetDefaultSmdpAddressCallback callback);
- oneway void getRulesAuthTable(String callingPackage, in IGetRulesAuthTableCallback callback);
- oneway void getEuiccChallenge(String callingPackage, in IGetEuiccChallengeCallback callback);
- oneway void getEuiccInfo1(String callingPackage, in IGetEuiccInfo1Callback callback);
- oneway void getEuiccInfo2(String callingPackage, in IGetEuiccInfo2Callback callback);
- oneway void authenticateServer(String callingPackage, String matchingId,
+ oneway void getRulesAuthTable(String callingPackage, String cardId,
+ in IGetRulesAuthTableCallback callback);
+ oneway void getEuiccChallenge(String callingPackage, String cardId,
+ in IGetEuiccChallengeCallback callback);
+ oneway void getEuiccInfo1(String callingPackage, String cardId,
+ in IGetEuiccInfo1Callback callback);
+ oneway void getEuiccInfo2(String callingPackage, String cardId,
+ in IGetEuiccInfo2Callback callback);
+ oneway void authenticateServer(String callingPackage, String cardId, String matchingId,
in byte[] serverSigned1, in byte[] serverSignature1, in byte[] euiccCiPkIdToBeUsed,
in byte[] serverCertificatein, in IAuthenticateServerCallback callback);
- oneway void prepareDownload(String callingPackage, in byte[] hashCc, in byte[] smdpSigned2,
- in byte[] smdpSignature2, in byte[] smdpCertificate, in IPrepareDownloadCallback callback);
- oneway void loadBoundProfilePackage(String callingPackage, in byte[] boundProfilePackage,
- in ILoadBoundProfilePackageCallback callback);
- oneway void cancelSession(String callingPackage, in byte[] transactionId, int reason,
- in ICancelSessionCallback callback);
- oneway void listNotifications(String callingPackage, int events,
+ oneway void prepareDownload(String callingPackage, String cardId, in byte[] hashCc,
+ in byte[] smdpSigned2, in byte[] smdpSignature2, in byte[] smdpCertificate,
+ in IPrepareDownloadCallback callback);
+ oneway void loadBoundProfilePackage(String callingPackage, String cardId,
+ in byte[] boundProfilePackage, in ILoadBoundProfilePackageCallback callback);
+ oneway void cancelSession(String callingPackage, String cardId, in byte[] transactionId,
+ int reason, in ICancelSessionCallback callback);
+ oneway void listNotifications(String callingPackage, String cardId, int events,
in IListNotificationsCallback callback);
- oneway void retrieveNotificationList(String callingPackage, int events,
+ oneway void retrieveNotificationList(String callingPackage, String cardId, int events,
in IRetrieveNotificationListCallback callback);
- oneway void retrieveNotification(String callingPackage, int seqNumber,
+ oneway void retrieveNotification(String callingPackage, String cardId, int seqNumber,
in IRetrieveNotificationCallback callback);
- oneway void removeNotificationFromList(String callingPackage, int seqNumber,
+ oneway void removeNotificationFromList(String callingPackage, String cardId, int seqNumber,
in IRemoveNotificationFromListCallback callback);
}
diff --git a/test-mock/src/android/test/mock/MockPackageManager.java b/test-mock/src/android/test/mock/MockPackageManager.java
index 41cde17..1ddc52c 100644
--- a/test-mock/src/android/test/mock/MockPackageManager.java
+++ b/test-mock/src/android/test/mock/MockPackageManager.java
@@ -1196,4 +1196,17 @@
public CharSequence getHarmfulAppWarning(String packageName) {
throw new UnsupportedOperationException();
}
+
+ @Override
+ public boolean hasSigningCertificate(
+ String packageName, byte[] certificate, @PackageManager.CertificateInputType int type) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean hasSigningCertificate(
+ int uid, byte[] certificate, @PackageManager.CertificateInputType int type) {
+ throw new UnsupportedOperationException();
+ }
+
}
diff --git a/tests/net/java/com/android/internal/net/NetworkStatsFactoryTest.java b/tests/net/java/com/android/internal/net/NetworkStatsFactoryTest.java
index 56b8e60..b14f550 100644
--- a/tests/net/java/com/android/internal/net/NetworkStatsFactoryTest.java
+++ b/tests/net/java/com/android/internal/net/NetworkStatsFactoryTest.java
@@ -67,7 +67,7 @@
IoUtils.deleteContents(mTestProc);
}
- mFactory = new NetworkStatsFactory(mTestProc);
+ mFactory = new NetworkStatsFactory(mTestProc, false);
}
@After
@@ -116,6 +116,20 @@
}
@Test
+ public void testNetworkStatsSummary() throws Exception {
+ stageFile(R.raw.net_dev_typical, file("net/dev"));
+
+ final NetworkStats stats = mFactory.readNetworkStatsIfaceDev();
+ assertEquals(6, stats.size());
+ assertStatsEntry(stats, "lo", UID_ALL, SET_ALL, TAG_NONE, 8308L, 8308L);
+ assertStatsEntry(stats, "rmnet0", UID_ALL, SET_ALL, TAG_NONE, 1507570L, 489339L);
+ assertStatsEntry(stats, "ifb0", UID_ALL, SET_ALL, TAG_NONE, 52454L, 0L);
+ assertStatsEntry(stats, "ifb1", UID_ALL, SET_ALL, TAG_NONE, 52454L, 0L);
+ assertStatsEntry(stats, "sit0", UID_ALL, SET_ALL, TAG_NONE, 0L, 0L);
+ assertStatsEntry(stats, "ip6tnl0", UID_ALL, SET_ALL, TAG_NONE, 0L, 0L);
+ }
+
+ @Test
public void testNetworkStatsSingle() throws Exception {
stageFile(R.raw.xt_qtaguid_iface_typical, file("net/xt_qtaguid/iface_stat_all"));
diff --git a/tests/net/res/raw/net_dev_typical b/tests/net/res/raw/net_dev_typical
new file mode 100644
index 0000000..290bf03
--- /dev/null
+++ b/tests/net/res/raw/net_dev_typical
@@ -0,0 +1,8 @@
+Inter-| Receive | Transmit
+ face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed
+ lo: 8308 116 0 0 0 0 0 0 8308 116 0 0 0 0 0 0
+rmnet0: 1507570 2205 0 0 0 0 0 0 489339 2237 0 0 0 0 0 0
+ ifb0: 52454 151 0 151 0 0 0 0 0 0 0 0 0 0 0 0
+ ifb1: 52454 151 0 151 0 0 0 0 0 0 0 0 0 0 0 0
+ sit0: 0 0 0 0 0 0 0 0 0 0 148 0 0 0 0 0
+ip6tnl0: 0 0 0 0 0 0 0 0 0 0 151 151 0 0 0 0
diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java
index eaad137..c46789c 100644
--- a/wifi/java/android/net/wifi/ScanResult.java
+++ b/wifi/java/android/net/wifi/ScanResult.java
@@ -294,18 +294,6 @@
}
/**
- * num IP configuration failures
- * @hide
- */
- public int numIpConfigFailures;
-
- /**
- * @hide
- * Last time we blacklisted the ScanResult
- */
- public long blackListTimestamp;
-
- /**
* Status indicating the scan result does not correspond to a user's saved configuration
* @hide
* @removed
@@ -314,12 +302,6 @@
public boolean untrusted;
/**
- * Number of time we connected to it
- * @hide
- */
- public int numConnection;
-
- /**
* Number of time autojoin used it
* @hide
*/
@@ -432,12 +414,6 @@
*/
public List<String> anqpLines;
- /**
- * @hide
- * storing the raw bytes of full result IEs
- **/
- public byte[] bytes;
-
/** information elements from beacon
* @hide
*/
@@ -612,9 +588,7 @@
distanceSdCm = source.distanceSdCm;
seen = source.seen;
untrusted = source.untrusted;
- numConnection = source.numConnection;
numUsage = source.numUsage;
- numIpConfigFailures = source.numIpConfigFailures;
venueName = source.venueName;
operatorFriendlyName = source.operatorFriendlyName;
flags = source.flags;
@@ -697,9 +671,7 @@
dest.writeInt(centerFreq1);
dest.writeLong(seen);
dest.writeInt(untrusted ? 1 : 0);
- dest.writeInt(numConnection);
dest.writeInt(numUsage);
- dest.writeInt(numIpConfigFailures);
dest.writeString((venueName != null) ? venueName.toString() : "");
dest.writeString((operatorFriendlyName != null) ? operatorFriendlyName.toString() : "");
dest.writeLong(this.flags);
@@ -779,9 +751,7 @@
sr.seen = in.readLong();
sr.untrusted = in.readInt() != 0;
- sr.numConnection = in.readInt();
sr.numUsage = in.readInt();
- sr.numIpConfigFailures = in.readInt();
sr.venueName = in.readString();
sr.operatorFriendlyName = in.readString();
sr.flags = in.readLong();