Merge "Handle remaining hidden API usages"
diff --git a/Android.bp b/Android.bp
index e63463c..1d93ee7 100644
--- a/Android.bp
+++ b/Android.bp
@@ -732,10 +732,13 @@
"ext",
],
+ jarjar_rules: ":framework-hidl-jarjar",
+
static_libs: [
"apex_aidl_interface-java",
"networkstack-aidl-interfaces-java",
"framework-protos",
+ "game-driver-protos",
"mediaplayer2-protos",
"android.hidl.base-V1.0-java",
"android.hardware.cas-V1.0-java",
@@ -790,6 +793,11 @@
],
}
+filegroup {
+ name: "framework-hidl-jarjar",
+ srcs: ["jarjar_rules_hidl.txt"],
+}
+
java_library {
name: "framework",
defaults: ["framework-defaults"],
diff --git a/api/current.txt b/api/current.txt
index 520d5bf..475f8e1 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -14619,8 +14619,8 @@
public class Picture {
ctor public Picture();
ctor public Picture(android.graphics.Picture);
- method public android.graphics.Canvas beginRecording(int, int);
- method public void draw(android.graphics.Canvas);
+ method @NonNull public android.graphics.Canvas beginRecording(int, int);
+ method public void draw(@NonNull android.graphics.Canvas);
method public void endRecording();
method public int getHeight();
method public int getWidth();
@@ -14872,7 +14872,7 @@
}
public final class RenderNode {
- ctor public RenderNode(String);
+ ctor public RenderNode(@Nullable String);
method public int computeApproximateMemoryUsage();
method public void discardDisplayList();
method public void endRecording();
@@ -22257,7 +22257,7 @@
method public void onUpdateExtractingViews(android.view.inputmethod.EditorInfo);
method public void onUpdateExtractingVisibility(android.view.inputmethod.EditorInfo);
method public void onUpdateSelection(int, int, int, int, int, int);
- method public void onViewClicked(boolean);
+ method @Deprecated public void onViewClicked(boolean);
method public void onWindowHidden();
method public void onWindowShown();
method public void requestHideSelf(int);
@@ -24639,6 +24639,7 @@
method @android.media.MediaDrm.SecurityLevel public int getSecurityLevel(@NonNull byte[]);
method public static boolean isCryptoSchemeSupported(@NonNull java.util.UUID);
method public static boolean isCryptoSchemeSupported(@NonNull java.util.UUID, @NonNull String);
+ method public static boolean isCryptoSchemeSupported(@NonNull java.util.UUID, @NonNull String, @android.media.MediaDrm.SecurityLevel int);
method @NonNull public byte[] openSession() throws android.media.NotProvisionedException, android.media.ResourceBusyException;
method @NonNull public byte[] openSession(@android.media.MediaDrm.SecurityLevel int) throws android.media.NotProvisionedException, android.media.ResourceBusyException;
method @Nullable public byte[] provideKeyResponse(@NonNull byte[], @NonNull byte[]) throws android.media.DeniedByServerException, android.media.NotProvisionedException;
@@ -44195,6 +44196,7 @@
public final class CellSignalStrengthGsm extends android.telephony.CellSignalStrength implements android.os.Parcelable {
method public int describeContents();
method public int getAsuLevel();
+ method public int getBitErrorRate();
method public int getDbm();
method public int getLevel();
method public int getTimingAdvance();
@@ -44496,16 +44498,16 @@
public class SignalStrength implements android.os.Parcelable {
method public int describeContents();
- method public int getCdmaDbm();
- method public int getCdmaEcio();
+ method @Deprecated public int getCdmaDbm();
+ method @Deprecated public int getCdmaEcio();
method @NonNull public java.util.List<android.telephony.CellSignalStrength> getCellSignalStrengths();
- method public int getEvdoDbm();
- method public int getEvdoEcio();
- method public int getEvdoSnr();
- method public int getGsmBitErrorRate();
- method public int getGsmSignalStrength();
+ method @Deprecated public int getEvdoDbm();
+ method @Deprecated public int getEvdoEcio();
+ method @Deprecated public int getEvdoSnr();
+ method @Deprecated public int getGsmBitErrorRate();
+ method @Deprecated public int getGsmSignalStrength();
method public int getLevel();
- method public boolean isGsm();
+ method @Deprecated public boolean isGsm();
method public void writeToParcel(android.os.Parcel, int);
field public static final int INVALID = 2147483647; // 0x7fffffff
}
@@ -52998,7 +53000,7 @@
method public void updateCursorAnchorInfo(android.view.View, android.view.inputmethod.CursorAnchorInfo);
method public void updateExtractedText(android.view.View, int, android.view.inputmethod.ExtractedText);
method public void updateSelection(android.view.View, int, int, int, int);
- method public void viewClicked(android.view.View);
+ method @Deprecated public void viewClicked(android.view.View);
field public static final int HIDE_IMPLICIT_ONLY = 1; // 0x1
field public static final int HIDE_NOT_ALWAYS = 2; // 0x2
field public static final int RESULT_HIDDEN = 3; // 0x3
diff --git a/api/removed.txt b/api/removed.txt
index e232227..9f4b041 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -239,8 +239,8 @@
}
public class Picture {
- method @Deprecated public static android.graphics.Picture createFromStream(java.io.InputStream);
- method @Deprecated public void writeToStream(java.io.OutputStream);
+ method @Deprecated public static android.graphics.Picture createFromStream(@NonNull java.io.InputStream);
+ method @Deprecated public void writeToStream(@NonNull java.io.OutputStream);
}
@Deprecated public class PixelXorXfermode extends android.graphics.Xfermode {
diff --git a/api/system-current.txt b/api/system-current.txt
index 16d7675..8377023 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1095,6 +1095,8 @@
public static final class UsageEvents.Event {
method public int getInstanceId();
method public String getNotificationChannelId();
+ method @Nullable public String getTaskRootClassName();
+ method @Nullable public String getTaskRootPackageName();
field public static final int NOTIFICATION_INTERRUPTION = 12; // 0xc
field public static final int NOTIFICATION_SEEN = 10; // 0xa
field public static final int SLICE_PINNED = 14; // 0xe
@@ -1109,6 +1111,7 @@
public final class UsageStatsManager {
method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getAppStandbyBucket(String);
method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public java.util.Map<java.lang.String,java.lang.Integer> getAppStandbyBuckets();
+ method public int getUsageSource();
method @RequiresPermission(android.Manifest.permission.OBSERVE_APP_USAGE) public void registerAppUsageObserver(int, @NonNull String[], long, @NonNull java.util.concurrent.TimeUnit, @NonNull android.app.PendingIntent);
method @RequiresPermission(android.Manifest.permission.OBSERVE_APP_USAGE) public void registerUsageSessionObserver(int, @NonNull String[], long, @NonNull java.util.concurrent.TimeUnit, long, @NonNull java.util.concurrent.TimeUnit, @NonNull android.app.PendingIntent, @Nullable android.app.PendingIntent);
method public void reportUsageStart(@NonNull android.app.Activity, @NonNull String);
@@ -1124,6 +1127,8 @@
field public static final String EXTRA_TIME_USED = "android.app.usage.extra.TIME_USED";
field public static final int STANDBY_BUCKET_EXEMPTED = 5; // 0x5
field public static final int STANDBY_BUCKET_NEVER = 50; // 0x32
+ field public static final int USAGE_SOURCE_CURRENT_ACTIVITY = 2; // 0x2
+ field public static final int USAGE_SOURCE_TASK_ROOT_ACTIVITY = 1; // 0x1
}
}
@@ -7655,6 +7660,7 @@
method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public int getRadioPowerState();
method public int getSimApplicationState();
method public int getSimCardState();
+ method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getSimLocale();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getSupportedRadioAccessFamily();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public java.util.List<android.telephony.TelephonyHistogram> getTelephonyHistograms();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.UiccCardInfo[] getUiccCardsInfo();
diff --git a/api/test-current.txt b/api/test-current.txt
index eca7a7a..1f894c2 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -352,6 +352,10 @@
method public boolean isReservedSupported(@NonNull java.util.UUID);
}
+ public final class UsageStatsManager {
+ method public void forceUsageSourceSettingRead();
+ }
+
}
package android.bluetooth {
@@ -2273,6 +2277,10 @@
method public static int getLongPressTooltipHideTimeout();
}
+ public class ViewDebug {
+ method @Nullable public static AutoCloseable startRenderingCommandsCapture(android.view.View, java.util.concurrent.Executor, java.util.function.Function<android.graphics.Picture,java.lang.Boolean>);
+ }
+
public interface WindowManager extends android.view.ViewManager {
method public default void setShouldShowIme(int, boolean);
method public default void setShouldShowSystemDecors(int, boolean);
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 1942b2e..60b2e25 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -27,6 +27,7 @@
import "frameworks/base/core/proto/android/bluetooth/enums.proto";
import "frameworks/base/core/proto/android/bluetooth/hci/enums.proto";
import "frameworks/base/core/proto/android/bluetooth/hfp/enums.proto";
+import "frameworks/base/core/proto/android/debug/enums.proto";
import "frameworks/base/core/proto/android/net/networkcapabilities.proto";
import "frameworks/base/core/proto/android/os/enums.proto";
import "frameworks/base/core/proto/android/server/connectivity/data_stall_event.proto";
@@ -204,6 +205,7 @@
SeOmapiReported se_omapi_reported = 141;
BroadcastDispatchLatencyReported broadcast_dispatch_latency_reported = 142;
AttentionManagerServiceResultReported attention_manager_service_result_reported = 143;
+ AdbConnectionChanged adb_connection_changed = 144;
}
// Pulled events will start at field 10000.
@@ -4466,7 +4468,7 @@
// SIMx or eSEx.
optional string terminal = 2;
- optional string packageName = 3;
+ optional string package_name = 3;
}
/**
@@ -4497,3 +4499,25 @@
}
optional AttentionCheckResult attention_check_result = 1 [default = UNKNOWN];
}
+
+/**
+ * Logs when an adb connection changes state.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/adb/AdbDebuggingManager.java
+ */
+message AdbConnectionChanged {
+ // The last time this system connected via adb, or 0 if the 'always allow' option was not
+ // previously selected for this system.
+ optional int64 last_connection_time_millis = 1;
+
+ // The time in ms within which a subsequent connection from an 'always allow' system is allowed
+ // to reconnect via adb without user interaction.
+ optional int64 auth_window_millis = 2;
+
+ // The state of the adb connection from frameworks/base/core/proto/android/debug/enums.proto.
+ optional android.debug.AdbConnectionStateEnum state = 3;
+
+ // True if the 'always allow' option was selected for this system.
+ optional bool always_allow = 4;
+}
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 5cac048..86e658d 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -219,9 +219,11 @@
* @param userId
* @param event
* @param appToken ActivityRecord's appToken.
+ * @param taskRoot TaskRecord's root
*/
public abstract void updateActivityUsageStats(
- ComponentName activity, int userId, int event, IBinder appToken);
+ ComponentName activity, int userId, int event, IBinder appToken,
+ ComponentName taskRoot);
public abstract void updateForegroundTimeIfOnBattery(
String packageName, int uid, long cpuTimeDiff);
public abstract void sendForegroundProfileChanged(int userId);
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 5868771f..ab2430c 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -27,6 +27,7 @@
import android.annotation.UnsupportedAppUsage;
import android.app.usage.UsageStatsManager;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.database.ContentObserver;
import android.media.AudioAttributes.AttributeUsage;
@@ -41,9 +42,9 @@
import android.os.UserManager;
import android.provider.Settings;
import android.util.ArrayMap;
+import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
-import android.util.SparseArray;
import com.android.internal.app.IAppOpsActiveCallback;
import com.android.internal.app.IAppOpsCallback;
import com.android.internal.app.IAppOpsNotedCallback;
@@ -60,8 +61,8 @@
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
-import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
/**
@@ -1489,7 +1490,7 @@
AppOpsManager.MODE_ALLOWED, // READ_ICC_SMS
AppOpsManager.MODE_ALLOWED, // WRITE_ICC_SMS
AppOpsManager.MODE_DEFAULT, // WRITE_SETTINGS
- AppOpsManager.MODE_DEFAULT, // SYSTEM_ALERT_WINDOW
+ getSystemAlertWindowDefault(), // SYSTEM_ALERT_WINDOW
AppOpsManager.MODE_ALLOWED, // ACCESS_NOTIFICATIONS
AppOpsManager.MODE_ALLOWED, // CAMERA
AppOpsManager.MODE_ALLOWED, // RECORD_AUDIO
@@ -4816,4 +4817,21 @@
}
}
}
+
+ private static int getSystemAlertWindowDefault() {
+ final Context context = ActivityThread.currentApplication();
+ if (context == null) {
+ return AppOpsManager.MODE_DEFAULT;
+ }
+
+ // system alert window is disable on low ram phones starting from Q
+ final PackageManager pm = context.getPackageManager();
+ // TVs are constantly plugged in and has less concern for memory/power
+ if (ActivityManager.isLowRamDeviceStatic()
+ && !pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK, 0)) {
+ return AppOpsManager.MODE_IGNORED;
+ }
+
+ return AppOpsManager.MODE_DEFAULT;
+ }
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index b0a08ed..55a3acb 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -9518,18 +9518,12 @@
}
/**
- * Allows the device owner or profile owner to enable or disable the backup service.
+ * Allows the device owner to enable or disable the backup service.
*
- * <p> Each user has its own backup service which manages the backup and restore mechanisms in
- * that user. Disabling the backup service will prevent data from being backed up or restored.
+ * <p> Backup service manages all backup and restore mechanisms on the device. Setting this to
+ * false will prevent data from being backed up or restored.
*
- * <p> Device owner calls this API to control backup services across all users on the device.
- * Profile owner can use this API to enable or disable the profile's backup service. However,
- * for a managed profile its backup functionality is only enabled if both the device owner
- * and the profile owner have enabled the backup service.
- *
- * <p> By default, backup service is disabled on a device with device owner, and within a
- * managed profile.
+ * <p> Backup service is off by default when device owner is present.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @param enabled {@code true} to enable the backup service, {@code false} to disable it.
@@ -9545,12 +9539,7 @@
}
/**
- * Return whether the backup service is enabled by the device owner or profile owner for the
- * current user, as previously set by {@link #setBackupServiceEnabled(ComponentName, boolean)}.
- *
- * <p> Whether the backup functionality is actually enabled or not depends on settings from both
- * the current user and the device owner, please see
- * {@link #setBackupServiceEnabled(ComponentName, boolean)} for details.
+ * Return whether the backup service is enabled by the device owner.
*
* <p> Backup service manages all backup and restore mechanisms on the device.
*
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index c983d4f..24580b4 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -16,6 +16,7 @@
package android.app.backup;
+import android.annotation.Nullable;
import android.app.IBackupAgent;
import android.app.QueuedWork;
import android.app.backup.FullBackup.BackupScheme.PathWithRequiredFlags;
@@ -181,6 +182,8 @@
Handler mHandler = null;
+ @Nullable private UserHandle mUser;
+
Handler getHandler() {
if (mHandler == null) {
mHandler = new Handler(Looper.getMainLooper());
@@ -232,6 +235,8 @@
*/
public void onCreate(UserHandle user) {
onCreate();
+
+ mUser = user;
}
/**
@@ -528,6 +533,10 @@
public void onQuotaExceeded(long backupDataBytes, long quotaBytes) {
}
+ private int getBackupUserId() {
+ return mUser == null ? super.getUserId() : mUser.getIdentifier();
+ }
+
/**
* Check whether the xml yielded any <include/> tag for the provided <code>domainToken</code>.
* If so, perform a {@link #fullBackupFileTree} which backs up the file or recurses if the path
@@ -1033,7 +1042,7 @@
Binder.restoreCallingIdentity(ident);
try {
- callbackBinder.opComplete(token, 0);
+ callbackBinder.opCompleteForUser(getBackupUserId(), token, 0);
} catch (RemoteException e) {
// we'll time out anyway, so we're safe
}
@@ -1082,7 +1091,7 @@
Binder.restoreCallingIdentity(ident);
try {
- callbackBinder.opComplete(token, 0);
+ callbackBinder.opCompleteForUser(getBackupUserId(), token, 0);
} catch (RemoteException e) {
// we'll time out anyway, so we're safe
}
@@ -1112,7 +1121,8 @@
} finally {
Binder.restoreCallingIdentity(ident);
try {
- callbackBinder.opComplete(token, measureOutput.getSize());
+ callbackBinder.opCompleteForUser(getBackupUserId(), token,
+ measureOutput.getSize());
} catch (RemoteException e) {
// timeout, so we're safe
}
@@ -1137,7 +1147,7 @@
Binder.restoreCallingIdentity(ident);
try {
- callbackBinder.opComplete(token, 0);
+ callbackBinder.opCompleteForUser(getBackupUserId(), token, 0);
} catch (RemoteException e) {
// we'll time out anyway, so we're safe
}
@@ -1162,7 +1172,7 @@
Binder.restoreCallingIdentity(ident);
try {
- callbackBinder.opComplete(token, 0);
+ callbackBinder.opCompleteForUser(getBackupUserId(), token, 0);
} catch (RemoteException e) {
// we'll time out anyway, so we're safe
}
diff --git a/core/java/android/app/backup/IBackupManager.aidl b/core/java/android/app/backup/IBackupManager.aidl
index f8c5a81..eda8981 100644
--- a/core/java/android/app/backup/IBackupManager.aidl
+++ b/core/java/android/app/backup/IBackupManager.aidl
@@ -549,6 +549,19 @@
/**
* Notify the backup manager that a BackupAgent has completed the operation
+ * corresponding to the given token and user id.
+ *
+ * @param userId User id for which the operation has been completed.
+ * @param token The transaction token passed to the BackupAgent method being
+ * invoked.
+ * @param result In the case of a full backup measure operation, the estimated
+ * total file size that would result from the operation. Unused in all other
+ * cases.
+ */
+ void opCompleteForUser(int userId, int token, long result);
+
+ /**
+ * Notify the backup manager that a BackupAgent has completed the operation
* corresponding to the given token.
*
* @param token The transaction token passed to the BackupAgent method being
diff --git a/core/java/android/app/usage/IUsageStatsManager.aidl b/core/java/android/app/usage/IUsageStatsManager.aidl
index bbae7d3..d2934b9 100644
--- a/core/java/android/app/usage/IUsageStatsManager.aidl
+++ b/core/java/android/app/usage/IUsageStatsManager.aidl
@@ -59,4 +59,6 @@
void reportPastUsageStart(in IBinder activity, String token, long timeAgoMs,
String callingPackage);
void reportUsageStop(in IBinder activity, String token, String callingPackage);
+ int getUsageSource();
+ void forceUsageSourceSettingRead();
}
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index 2c5fe04..451f44b 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -16,6 +16,7 @@
package android.app.usage;
import android.annotation.IntDef;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.UnsupportedAppUsage;
import android.content.res.Configuration;
@@ -286,7 +287,6 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public String mClass;
-
/**
* {@hide}
*/
@@ -295,6 +295,16 @@
/**
* {@hide}
*/
+ public String mTaskRootPackage;
+
+ /**
+ * {@hide}
+ */
+ public String mTaskRootClass;
+
+ /**
+ * {@hide}
+ */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public long mTimeStamp;
@@ -373,6 +383,8 @@
mPackage = orig.mPackage;
mClass = orig.mClass;
mInstanceId = orig.mInstanceId;
+ mTaskRootPackage = orig.mTaskRootPackage;
+ mTaskRootClass = orig.mTaskRootClass;
mTimeStamp = orig.mTimeStamp;
mEventType = orig.mEventType;
mConfiguration = orig.mConfiguration;
@@ -411,6 +423,28 @@
}
/**
+ * The package name of the task root when this event was reported.
+ * Or {@code null} for queries from apps without {@link
+ * android.Manifest.permission#PACKAGE_USAGE_STATS}
+ * @hide
+ */
+ @SystemApi
+ public @Nullable String getTaskRootPackageName() {
+ return mTaskRootPackage;
+ }
+
+ /**
+ * The class name of the task root when this event was reported.
+ * Or {@code null} for queries from apps without {@link
+ * android.Manifest.permission#PACKAGE_USAGE_STATS}
+ * @hide
+ */
+ @SystemApi
+ public @Nullable String getTaskRootClassName() {
+ return mTaskRootClass;
+ }
+
+ /**
* The time at which this event occurred, measured in milliseconds since the epoch.
* <p/>
* See {@link System#currentTimeMillis()}.
@@ -522,6 +556,9 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private int mIndex = 0;
+ // Only used when parceling events. If false, task roots will be omitted from the parcel
+ private final boolean mIncludeTaskRoots;
+
/*
* In order to save space, since ComponentNames will be duplicated everywhere,
* we use a map and index into it.
@@ -552,6 +589,7 @@
mParcel.setDataSize(mParcel.dataPosition());
mParcel.setDataPosition(positionInParcel);
}
+ mIncludeTaskRoots = true;
}
/**
@@ -560,16 +598,27 @@
*/
UsageEvents() {
mEventCount = 0;
+ mIncludeTaskRoots = true;
+ }
+
+ /**
+ * Construct the iterator in preparation for writing it to a parcel.
+ * Defaults to excluding task roots from the parcel.
+ * {@hide}
+ */
+ public UsageEvents(List<Event> events, String[] stringPool) {
+ this(events, stringPool, false);
}
/**
* Construct the iterator in preparation for writing it to a parcel.
* {@hide}
*/
- public UsageEvents(List<Event> events, String[] stringPool) {
+ public UsageEvents(List<Event> events, String[] stringPool, boolean includeTaskRoots) {
mStringPool = stringPool;
mEventCount = events.size();
mEventsToWrite = events;
+ mIncludeTaskRoots = includeTaskRoots;
}
/**
@@ -645,9 +694,25 @@
} else {
classIndex = -1;
}
+
+ final int taskRootPackageIndex;
+ if (mIncludeTaskRoots && event.mTaskRootPackage != null) {
+ taskRootPackageIndex = findStringIndex(event.mTaskRootPackage);
+ } else {
+ taskRootPackageIndex = -1;
+ }
+
+ final int taskRootClassIndex;
+ if (mIncludeTaskRoots && event.mTaskRootClass != null) {
+ taskRootClassIndex = findStringIndex(event.mTaskRootClass);
+ } else {
+ taskRootClassIndex = -1;
+ }
p.writeInt(packageIndex);
p.writeInt(classIndex);
p.writeInt(event.mInstanceId);
+ p.writeInt(taskRootPackageIndex);
+ p.writeInt(taskRootClassIndex);
p.writeInt(event.mEventType);
p.writeLong(event.mTimeStamp);
@@ -691,6 +756,21 @@
eventOut.mClass = null;
}
eventOut.mInstanceId = p.readInt();
+
+ final int taskRootPackageIndex = p.readInt();
+ if (taskRootPackageIndex >= 0) {
+ eventOut.mTaskRootPackage = mStringPool[taskRootPackageIndex];
+ } else {
+ eventOut.mTaskRootPackage = null;
+ }
+
+ final int taskRootClassIndex = p.readInt();
+ if (taskRootClassIndex >= 0) {
+ eventOut.mTaskRootClass = mStringPool[taskRootClassIndex];
+ } else {
+ eventOut.mTaskRootClass = null;
+ }
+
eventOut.mEventType = p.readInt();
eventOut.mTimeStamp = p.readLong();
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index 605deac..d2de887 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -22,6 +22,7 @@
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
+import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.app.Activity;
import android.app.PendingIntent;
@@ -234,6 +235,29 @@
@SystemApi
public static final String EXTRA_TIME_USED = "android.app.usage.extra.TIME_USED";
+
+ /**
+ * App usage observers will consider the task root package the source of usage.
+ * @hide
+ */
+ @SystemApi
+ public static final int USAGE_SOURCE_TASK_ROOT_ACTIVITY = 1;
+
+ /**
+ * App usage observers will consider the visible activity's package the source of usage.
+ * @hide
+ */
+ @SystemApi
+ public static final int USAGE_SOURCE_CURRENT_ACTIVITY = 2;
+
+ /** @hide */
+ @IntDef(prefix = { "USAGE_SOURCE_" }, value = {
+ USAGE_SOURCE_TASK_ROOT_ACTIVITY,
+ USAGE_SOURCE_CURRENT_ACTIVITY,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface UsageSource {}
+
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private static final UsageEvents sEmptyResults = new UsageEvents();
@@ -776,6 +800,38 @@
}
}
+ /**
+ * Get what App Usage Observers will consider the source of usage for an activity. Usage Source
+ * is decided at boot and will not change until next boot.
+ * @see #USAGE_SOURCE_TASK_ROOT_ACTIVITY
+ * @see #USAGE_SOURCE_CURRENT_ACTIVITY
+ *
+ * @throws SecurityException if the caller doesn't have the OBSERVE_APP_USAGE permission and
+ * is not the profile owner of this user.
+ * @hide
+ */
+ @SystemApi
+ public @UsageSource int getUsageSource() {
+ try {
+ return mService.getUsageSource();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Force the Usage Source be reread from global settings.
+ * @hide
+ */
+ @TestApi
+ public void forceUsageSourceSettingRead() {
+ try {
+ mService.forceUsageSourceSettingRead();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/** @hide */
public static String reasonToString(int standbyReason) {
StringBuilder sb = new StringBuilder();
@@ -845,6 +901,22 @@
return sb.toString();
}
+ /** @hide */
+ public static String usageSourceToString(int usageSource) {
+ switch (usageSource) {
+ case USAGE_SOURCE_TASK_ROOT_ACTIVITY:
+ return "TASK_ROOT_ACTIVITY";
+ case USAGE_SOURCE_CURRENT_ACTIVITY:
+ return "CURRENT_ACTIVITY";
+ default:
+ StringBuilder sb = new StringBuilder();
+ sb.append("UNKNOWN(");
+ sb.append(usageSource);
+ sb.append(")");
+ return sb.toString();
+ }
+ }
+
/**
* {@hide}
* Temporarily whitelist the specified app for a short duration. This is to allow an app
diff --git a/core/java/android/app/usage/UsageStatsManagerInternal.java b/core/java/android/app/usage/UsageStatsManagerInternal.java
index cc3ab00..d2d0cf9c 100644
--- a/core/java/android/app/usage/UsageStatsManagerInternal.java
+++ b/core/java/android/app/usage/UsageStatsManagerInternal.java
@@ -40,9 +40,11 @@
* {@link UsageEvents}
* @param instanceId For activity, hashCode of ActivityRecord's appToken.
* For non-activity, it is not used.
+ * @param taskRoot For activity, the name of the package at the root of the task
+ * For non-activity, it is not used.
*/
public abstract void reportEvent(ComponentName component, @UserIdInt int userId, int eventType,
- int instanceId);
+ int instanceId, ComponentName taskRoot);
/**
* Reports an event to the UsageStatsManager.
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index d3509d5..38ddc16 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -2087,7 +2087,14 @@
* protocol, so applications with custom text editing written before this method appeared will
* not call to inform the IME of this interaction.
* @param focusChanged true if the user changed the focused view by this click.
+ * @see InputMethodManager#viewClicked(View)
+ * @deprecated The method may not be called for composite {@link View} that works as a giant
+ * "Canvas", which can host its own UI hierarchy and sub focus state.
+ * {@link android.webkit.WebView} is a good example. Application / IME developers
+ * should not rely on this method. If your goal is just being notified when an
+ * on-going input is interrupted, simply monitor {@link #onFinishInput()}.
*/
+ @Deprecated
public void onViewClicked(boolean focusChanged) {
// Intentionally empty
}
diff --git a/core/java/android/net/INetworkManagementEventObserver.aidl b/core/java/android/net/INetworkManagementEventObserver.aidl
index b7af374..f0fe92e 100644
--- a/core/java/android/net/INetworkManagementEventObserver.aidl
+++ b/core/java/android/net/INetworkManagementEventObserver.aidl
@@ -24,7 +24,7 @@
*
* @hide
*/
-interface INetworkManagementEventObserver {
+oneway interface INetworkManagementEventObserver {
/**
* Interface configuration status has changed.
*
diff --git a/core/java/android/net/INetworkStackConnector.aidl b/core/java/android/net/INetworkStackConnector.aidl
index 2df8ab7..8b64f1c 100644
--- a/core/java/android/net/INetworkStackConnector.aidl
+++ b/core/java/android/net/INetworkStackConnector.aidl
@@ -18,10 +18,12 @@
import android.net.INetworkMonitorCallbacks;
import android.net.dhcp.DhcpServingParamsParcel;
import android.net.dhcp.IDhcpServerCallbacks;
+import android.net.ip.IIpClientCallbacks;
/** @hide */
oneway interface INetworkStackConnector {
void makeDhcpServer(in String ifName, in DhcpServingParamsParcel params,
in IDhcpServerCallbacks cb);
void makeNetworkMonitor(int netId, String name, in INetworkMonitorCallbacks cb);
+ void makeIpClient(in String ifName, in IIpClientCallbacks callbacks);
}
\ No newline at end of file
diff --git a/core/java/android/net/NetworkStack.java b/core/java/android/net/NetworkStack.java
index af043ee..d277034 100644
--- a/core/java/android/net/NetworkStack.java
+++ b/core/java/android/net/NetworkStack.java
@@ -27,6 +27,7 @@
import android.content.ServiceConnection;
import android.net.dhcp.DhcpServingParamsParcel;
import android.net.dhcp.IDhcpServerCallbacks;
+import android.net.ip.IIpClientCallbacks;
import android.os.Binder;
import android.os.IBinder;
import android.os.Process;
@@ -84,6 +85,21 @@
}
/**
+ * Create an IpClient on the specified interface.
+ *
+ * <p>The IpClient will be returned asynchronously through the provided callbacks.
+ */
+ public void makeIpClient(String ifName, IIpClientCallbacks cb) {
+ requestConnector(connector -> {
+ try {
+ connector.makeIpClient(ifName, cb);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ });
+ }
+
+ /**
* Create a NetworkMonitor.
*
* <p>The INetworkMonitor will be returned asynchronously through the provided callbacks.
diff --git a/services/net/java/android/net/util/MultinetworkPolicyTracker.java b/core/java/android/net/util/MultinetworkPolicyTracker.java
similarity index 100%
rename from services/net/java/android/net/util/MultinetworkPolicyTracker.java
rename to core/java/android/net/util/MultinetworkPolicyTracker.java
diff --git a/core/java/android/os/AppZygote.java b/core/java/android/os/AppZygote.java
index 950f381..6daa5b4 100644
--- a/core/java/android/os/AppZygote.java
+++ b/core/java/android/os/AppZygote.java
@@ -103,7 +103,7 @@
String abi = mAppInfo.primaryCpuAbi != null ? mAppInfo.primaryCpuAbi :
Build.SUPPORTED_ABIS[0];
try {
- mZygote = Process.zygoteProcess.startChildZygote(
+ mZygote = Process.ZYGOTE_PROCESS.startChildZygote(
"com.android.internal.os.AppZygoteInit",
mAppInfo.processName + "_zygote",
mZygoteUid,
diff --git a/core/java/android/os/BugreportManager.java b/core/java/android/os/BugreportManager.java
index c5a51f1..518528d 100644
--- a/core/java/android/os/BugreportManager.java
+++ b/core/java/android/os/BugreportManager.java
@@ -79,23 +79,27 @@
* Called when taking bugreport resulted in an error.
*
* @param errorCode the error that occurred. Possible values are
- * {@code BUGREPORT_ERROR_INVALID_INPUT}, {@code BUGREPORT_ERROR_RUNTIME}.
+ * {@code BUGREPORT_ERROR_INVALID_INPUT},
+ * {@code BUGREPORT_ERROR_RUNTIME},
+ * {@code BUGREPORT_ERROR_USER_DENIED_CONSENT}.
*/
void onError(@BugreportErrorCode int errorCode);
/**
- * Called when taking bugreport finishes successfully
- *
- * @param durationMs time capturing bugreport took in milliseconds
- * @param title title for the bugreport; helpful in reminding the user why they took it
- * @param description detailed description for the bugreport
+ * Called when taking bugreport finishes successfully.
*/
- void onFinished(long durationMs, @NonNull String title,
- @NonNull String description);
+ void onFinished();
}
/**
- * Starts a bugreport asynchronously.
+ * Starts a bugreport.
+ *
+ * <p>This starts a bugreport in the background. However the call itself can take several
+ * seconds to return in the worst case. {@code listener} will receive progress and status
+ * updates.
+ *
+ * <p>The bugreport artifacts will be copied over to the given file descriptors only if the
+ * user consents to sharing with the calling app.
*
* @param bugreportFd file to write the bugreport. This should be opened in write-only,
* append mode.
@@ -107,7 +111,7 @@
@RequiresPermission(android.Manifest.permission.DUMP)
public void startBugreport(@NonNull FileDescriptor bugreportFd,
@Nullable FileDescriptor screenshotFd,
- @NonNull BugreportParams params, @Nullable BugreportListener listener) {
+ @NonNull BugreportParams params, @NonNull BugreportListener listener) {
// TODO(b/111441001): Enforce android.Manifest.permission.DUMP if necessary.
DumpstateListener dsListener = new DumpstateListener(listener);
@@ -121,6 +125,18 @@
}
}
+ /*
+ * Cancels a currently running bugreport.
+ */
+ @RequiresPermission(android.Manifest.permission.DUMP)
+ public void cancelBugreport() {
+ try {
+ mBinder.cancelBugreport();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
private final class DumpstateListener extends IDumpstateListener.Stub
implements DeathRecipient {
private final BugreportListener mListener;
@@ -145,9 +161,13 @@
}
@Override
- public void onFinished(long durationMs, String title, String description)
- throws RemoteException {
- mListener.onFinished(durationMs, title, description);
+ public void onFinished() throws RemoteException {
+ try {
+ mListener.onFinished();
+ } finally {
+ // The bugreport has finished. Let's shutdown the service to minimize its footprint.
+ cancelBugreport();
+ }
}
// Old methods; should go away
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index f51ba9a..5bf9095 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -23,10 +23,15 @@
import android.content.pm.ResolveInfo;
import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
+import android.gamedriver.GameDriverProto.Blacklist;
+import android.gamedriver.GameDriverProto.Blacklists;
import android.opengl.EGL14;
import android.provider.Settings;
+import android.util.Base64;
import android.util.Log;
+import com.android.framework.protobuf.InvalidProtocolBufferException;
+
import dalvik.system.VMRuntime;
import java.io.BufferedReader;
@@ -62,6 +67,8 @@
private static final String ANGLE_RULES_FILE = "a4a_rules.json";
private static final String ANGLE_TEMP_RULES = "debug.angle.rules";
private static final String ACTION_ANGLE_FOR_ANDROID = "android.app.action.ANGLE_FOR_ANDROID";
+ private static final String GAME_DRIVER_BLACKLIST_FLAG = "blacklist";
+ private static final int BASE64_FLAGS = Base64.NO_PADDING | Base64.NO_WRAP;
private ClassLoader mClassLoader;
private String mLayerPath;
@@ -480,6 +487,24 @@
return;
}
+ ApplicationInfo driverInfo;
+ try {
+ driverInfo = context.getPackageManager().getApplicationInfo(driverPackageName,
+ PackageManager.MATCH_SYSTEM_ONLY);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(TAG, "driver package '" + driverPackageName + "' not installed");
+ return;
+ }
+
+ // O drivers are restricted to the sphal linker namespace, so don't try to use
+ // packages unless they declare they're compatible with that restriction.
+ if (driverInfo.targetSdkVersion < Build.VERSION_CODES.O) {
+ if (DEBUG) {
+ Log.w(TAG, "updated driver package is not known to be compatible with O");
+ }
+ return;
+ }
+
// To minimize risk of driver updates crippling the device beyond user repair, never use an
// updated driver for privileged or non-updated system apps. Presumably pre-installed apps
// were tested thoroughly with the pre-installed driver.
@@ -491,7 +516,7 @@
// GUP_DEV_ALL_APPS
// 0: Default (Invalid values fallback to default as well)
- // 1: All apps use Game Update Package
+ // 1: All apps use Game Driver
// 2: All apps use system graphics driver
int gupDevAllApps = coreSettings.getInt(Settings.Global.GUP_DEV_ALL_APPS, 0);
if (gupDevAllApps == 2) {
@@ -510,33 +535,45 @@
}
return;
}
+ boolean isDevOptIn = getGlobalSettingsString(coreSettings,
+ Settings.Global.GUP_DEV_OPT_IN_APPS)
+ .contains(ai.packageName);
- if (!getGlobalSettingsString(coreSettings, Settings.Global.GUP_DEV_OPT_IN_APPS)
- .contains(ai.packageName)
- && !onWhitelist(context, driverPackageName, ai.packageName)) {
+ if (!isDevOptIn && !onWhitelist(context, driverPackageName, ai.packageName)) {
if (DEBUG) {
Log.w(TAG, ai.packageName + " is not on the whitelist.");
}
return;
}
- }
- ApplicationInfo driverInfo;
- try {
- driverInfo = context.getPackageManager().getApplicationInfo(driverPackageName,
- PackageManager.MATCH_SYSTEM_ONLY);
- } catch (PackageManager.NameNotFoundException e) {
- Log.w(TAG, "driver package '" + driverPackageName + "' not installed");
- return;
- }
-
- // O drivers are restricted to the sphal linker namespace, so don't try to use
- // packages unless they declare they're compatible with that restriction.
- if (driverInfo.targetSdkVersion < Build.VERSION_CODES.O) {
- if (DEBUG) {
- Log.w(TAG, "updated driver package is not known to be compatible with O");
+ if (!isDevOptIn) {
+ // At this point, the application is on the whitelist only, check whether it's
+ // on the blacklist, terminate early when it's on the blacklist.
+ try {
+ // TODO(b/121350991) Switch to DeviceConfig with property listener.
+ String base64String = coreSettings.getString(Settings.Global.GUP_BLACKLIST);
+ if (base64String != null && !base64String.isEmpty()) {
+ Blacklists blacklistsProto = Blacklists.parseFrom(
+ Base64.decode(base64String, BASE64_FLAGS));
+ List<Blacklist> blacklists = blacklistsProto.getBlacklistsList();
+ long driverVersionCode = driverInfo.longVersionCode;
+ for (Blacklist blacklist : blacklists) {
+ if (blacklist.getVersionCode() == driverVersionCode) {
+ for (String packageName : blacklist.getPackageNamesList()) {
+ if (packageName == ai.packageName) {
+ return;
+ }
+ }
+ break;
+ }
+ }
+ }
+ } catch (InvalidProtocolBufferException e) {
+ if (DEBUG) {
+ Log.w(TAG, "Can't parse blacklist, skip and continue...");
+ }
+ }
}
- return;
}
String abi = chooseAbi(driverInfo);
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index f2a9adb..07c4933 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -32,16 +32,6 @@
private static final String LOG_TAG = "Process";
/**
- * @hide for internal use only.
- */
- public static final String ZYGOTE_SOCKET = "zygote";
-
- /**
- * @hide for internal use only.
- */
- public static final String SECONDARY_ZYGOTE_SOCKET = "zygote_secondary";
-
- /**
* An invalid UID value.
*/
public static final int INVALID_UID = -1;
@@ -479,8 +469,7 @@
* State associated with the zygote process.
* @hide
*/
- public static final ZygoteProcess zygoteProcess =
- new ZygoteProcess(ZYGOTE_SOCKET, SECONDARY_ZYGOTE_SOCKET);
+ public static final ZygoteProcess ZYGOTE_PROCESS = new ZygoteProcess();
/**
* Start a new process.
@@ -538,7 +527,7 @@
@Nullable String[] packagesForUid,
@Nullable String[] visibleVols,
@Nullable String[] zygoteArgs) {
- return zygoteProcess.start(processClass, niceName, uid, gid, gids,
+ return ZYGOTE_PROCESS.start(processClass, niceName, uid, gid, gids,
runtimeFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, invokeWith, packageName,
packagesForUid, visibleVols, zygoteArgs);
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index ec77821..4cb9c5b 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -62,87 +62,119 @@
* {@hide}
*/
public class ZygoteProcess {
+
+ /**
+ * @hide for internal use only.
+ */
+ public static final String ZYGOTE_SOCKET_NAME = "zygote";
+
+ /**
+ * @hide for internal use only.
+ */
+ public static final String ZYGOTE_SECONDARY_SOCKET_NAME = "zygote_secondary";
+
+ /**
+ * @hide for internal use only
+ */
private static final String LOG_TAG = "ZygoteProcess";
/**
* The name of the socket used to communicate with the primary zygote.
*/
- private final LocalSocketAddress mSocket;
+ private final LocalSocketAddress mZygoteSocketAddress;
/**
* The name of the secondary (alternate ABI) zygote socket.
*/
- private final LocalSocketAddress mSecondarySocket;
+ private final LocalSocketAddress mZygoteSecondarySocketAddress;
- public ZygoteProcess(String primarySocket, String secondarySocket) {
- this(new LocalSocketAddress(primarySocket, LocalSocketAddress.Namespace.RESERVED),
- new LocalSocketAddress(secondarySocket, LocalSocketAddress.Namespace.RESERVED));
+ public ZygoteProcess() {
+ mZygoteSocketAddress =
+ new LocalSocketAddress(ZYGOTE_SOCKET_NAME, LocalSocketAddress.Namespace.RESERVED);
+ mZygoteSecondarySocketAddress =
+ new LocalSocketAddress(ZYGOTE_SECONDARY_SOCKET_NAME,
+ LocalSocketAddress.Namespace.RESERVED);
}
- public ZygoteProcess(LocalSocketAddress primarySocket, LocalSocketAddress secondarySocket) {
- mSocket = primarySocket;
- mSecondarySocket = secondarySocket;
+ public ZygoteProcess(LocalSocketAddress primarySocketAddress,
+ LocalSocketAddress secondarySocketAddress) {
+ mZygoteSocketAddress = primarySocketAddress;
+ mZygoteSecondarySocketAddress = secondarySocketAddress;
}
public LocalSocketAddress getPrimarySocketAddress() {
- return mSocket;
+ return mZygoteSocketAddress;
}
/**
* State for communicating with the zygote process.
*/
public static class ZygoteState {
- final LocalSocket socket;
- final DataInputStream inputStream;
- final BufferedWriter writer;
- final List<String> abiList;
+ final LocalSocketAddress mZygoteSocketAddress;
- boolean mClosed;
+ private final LocalSocket mZygoteSessionSocket;
- private ZygoteState(LocalSocket socket, DataInputStream inputStream,
- BufferedWriter writer, List<String> abiList) {
- this.socket = socket;
- this.inputStream = inputStream;
- this.writer = writer;
- this.abiList = abiList;
+ final DataInputStream mZygoteInputStream;
+ final BufferedWriter mZygoteOutputWriter;
+
+ private final List<String> mABIList;
+
+ private boolean mClosed;
+
+ private ZygoteState(LocalSocketAddress zygoteSocketAddress,
+ LocalSocket zygoteSessionSocket,
+ DataInputStream zygoteInputStream,
+ BufferedWriter zygoteOutputWriter,
+ List<String> abiList) {
+ this.mZygoteSocketAddress = zygoteSocketAddress;
+ this.mZygoteSessionSocket = zygoteSessionSocket;
+ this.mZygoteInputStream = zygoteInputStream;
+ this.mZygoteOutputWriter = zygoteOutputWriter;
+ this.mABIList = abiList;
}
- public static ZygoteState connect(LocalSocketAddress address) throws IOException {
+ /**
+ * Create a new ZygoteState object by connecting to the given Zygote socket.
+ *
+ * @param zygoteSocketAddress Zygote socket to connect to
+ * @return A new ZygoteState object containing a session socket for the given Zygote socket
+ * address
+ * @throws IOException
+ */
+ public static ZygoteState connect(LocalSocketAddress zygoteSocketAddress)
+ throws IOException {
+
DataInputStream zygoteInputStream = null;
- BufferedWriter zygoteWriter = null;
- final LocalSocket zygoteSocket = new LocalSocket();
+ BufferedWriter zygoteOutputWriter = null;
+ final LocalSocket zygoteSessionSocket = new LocalSocket();
try {
- zygoteSocket.connect(address);
-
- zygoteInputStream = new DataInputStream(zygoteSocket.getInputStream());
-
- zygoteWriter = new BufferedWriter(new OutputStreamWriter(
- zygoteSocket.getOutputStream()), 256);
+ zygoteSessionSocket.connect(zygoteSocketAddress);
+ zygoteInputStream = new DataInputStream(zygoteSessionSocket.getInputStream());
+ zygoteOutputWriter =
+ new BufferedWriter(
+ new OutputStreamWriter(zygoteSessionSocket.getOutputStream()),
+ 256);
} catch (IOException ex) {
try {
- zygoteSocket.close();
- } catch (IOException ignore) {
- }
+ zygoteSessionSocket.close();
+ } catch (IOException ignore) { }
throw ex;
}
- String abiListString = getAbiList(zygoteWriter, zygoteInputStream);
- Log.i("Zygote", "Process: zygote socket " + address.getNamespace() + "/"
- + address.getName() + " opened, supported ABIS: " + abiListString);
-
- return new ZygoteState(zygoteSocket, zygoteInputStream, zygoteWriter,
- Arrays.asList(abiListString.split(",")));
+ return new ZygoteState(zygoteSocketAddress,
+ zygoteSessionSocket, zygoteInputStream, zygoteOutputWriter,
+ getAbiList(zygoteOutputWriter, zygoteInputStream));
}
boolean matches(String abi) {
- return abiList.contains(abi);
+ return mABIList.contains(abi);
}
public void close() {
try {
- socket.close();
+ mZygoteSessionSocket.close();
} catch (IOException ex) {
Log.e(LOG_TAG,"I/O exception on routine close", ex);
}
@@ -241,7 +273,7 @@
try {
return startViaZygote(processClass, niceName, uid, gid, gids,
runtimeFlags, mountExternal, targetSdkVersion, seInfo,
- abi, instructionSet, appDataDir, invokeWith, false /* startChildZygote */,
+ abi, instructionSet, appDataDir, invokeWith, /*startChildZygote=*/false,
packageName, packagesForUid, visibleVols, zygoteArgs);
} catch (ZygoteStartFailedEx ex) {
Log.e(LOG_TAG,
@@ -260,7 +292,7 @@
* @throws ZygoteStartFailedEx if the query failed.
*/
@GuardedBy("mLock")
- private static String getAbiList(BufferedWriter writer, DataInputStream inputStream)
+ private static List<String> getAbiList(BufferedWriter writer, DataInputStream inputStream)
throws IOException {
// Each query starts with the argument count (1 in this case)
writer.write("1");
@@ -276,7 +308,9 @@
byte[] bytes = new byte[numBytes];
inputStream.readFully(bytes);
- return new String(bytes, StandardCharsets.US_ASCII);
+ String rawList = new String(bytes, StandardCharsets.US_ASCII);
+
+ return Arrays.asList(rawList.split(","));
}
/**
@@ -310,8 +344,8 @@
* the child or -1 on failure, followed by boolean to
* indicate whether a wrapper process was used.
*/
- final BufferedWriter writer = zygoteState.writer;
- final DataInputStream inputStream = zygoteState.inputStream;
+ final BufferedWriter writer = zygoteState.mZygoteOutputWriter;
+ final DataInputStream inputStream = zygoteState.mZygoteInputStream;
writer.write(Integer.toString(args.size()));
writer.newLine();
@@ -528,18 +562,18 @@
ZygoteState state = openZygoteSocketIfNeeded(abi);
// Each query starts with the argument count (1 in this case)
- state.writer.write("1");
+ state.mZygoteOutputWriter.write("1");
// ... followed by a new-line.
- state.writer.newLine();
+ state.mZygoteOutputWriter.newLine();
// ... followed by our only argument.
- state.writer.write("--get-pid");
- state.writer.newLine();
- state.writer.flush();
+ state.mZygoteOutputWriter.write("--get-pid");
+ state.mZygoteOutputWriter.newLine();
+ state.mZygoteOutputWriter.flush();
// The response is a length prefixed stream of ASCII bytes.
- int numBytes = state.inputStream.readInt();
+ int numBytes = state.mZygoteInputStream.readInt();
byte[] bytes = new byte[numBytes];
- state.inputStream.readFully(bytes);
+ state.mZygoteInputStream.readFully(bytes);
return Integer.parseInt(new String(bytes, StandardCharsets.US_ASCII));
}
@@ -593,16 +627,16 @@
return true;
}
try {
- state.writer.write(Integer.toString(mApiBlacklistExemptions.size() + 1));
- state.writer.newLine();
- state.writer.write("--set-api-blacklist-exemptions");
- state.writer.newLine();
+ state.mZygoteOutputWriter.write(Integer.toString(mApiBlacklistExemptions.size() + 1));
+ state.mZygoteOutputWriter.newLine();
+ state.mZygoteOutputWriter.write("--set-api-blacklist-exemptions");
+ state.mZygoteOutputWriter.newLine();
for (int i = 0; i < mApiBlacklistExemptions.size(); ++i) {
- state.writer.write(mApiBlacklistExemptions.get(i));
- state.writer.newLine();
+ state.mZygoteOutputWriter.write(mApiBlacklistExemptions.get(i));
+ state.mZygoteOutputWriter.newLine();
}
- state.writer.flush();
- int status = state.inputStream.readInt();
+ state.mZygoteOutputWriter.flush();
+ int status = state.mZygoteInputStream.readInt();
if (status != 0) {
Slog.e(LOG_TAG, "Failed to set API blacklist exemptions; status " + status);
}
@@ -622,13 +656,13 @@
return;
}
try {
- state.writer.write(Integer.toString(1));
- state.writer.newLine();
- state.writer.write("--hidden-api-log-sampling-rate="
+ state.mZygoteOutputWriter.write(Integer.toString(1));
+ state.mZygoteOutputWriter.newLine();
+ state.mZygoteOutputWriter.write("--hidden-api-log-sampling-rate="
+ Integer.toString(mHiddenApiAccessLogSampleRate));
- state.writer.newLine();
- state.writer.flush();
- int status = state.inputStream.readInt();
+ state.mZygoteOutputWriter.newLine();
+ state.mZygoteOutputWriter.flush();
+ int status = state.mZygoteInputStream.readInt();
if (status != 0) {
Slog.e(LOG_TAG, "Failed to set hidden API log sampling rate; status " + status);
}
@@ -638,22 +672,29 @@
}
/**
- * Tries to open socket to Zygote process if not already open. If
- * already open, does nothing. May block and retry. Requires that mLock be held.
+ * Tries to open a session socket to a Zygote process with a compatible ABI if one is not
+ * already open. If a compatible session socket is already open that session socket is returned.
+ * This function may block and may have to try connecting to multiple Zygotes to find the
+ * appropriate one. Requires that mLock be held.
*/
@GuardedBy("mLock")
- private ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx {
+ private ZygoteState openZygoteSocketIfNeeded(String abi)
+ throws ZygoteStartFailedEx {
+
Preconditions.checkState(Thread.holdsLock(mLock), "ZygoteProcess lock not held");
if (primaryZygoteState == null || primaryZygoteState.isClosed()) {
try {
- primaryZygoteState = ZygoteState.connect(mSocket);
+ primaryZygoteState =
+ ZygoteState.connect(mZygoteSocketAddress);
} catch (IOException ioe) {
throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe);
}
+
maybeSetApiBlacklistExemptions(primaryZygoteState, false);
maybeSetHiddenApiAccessLogSampleRate(primaryZygoteState);
}
+
if (primaryZygoteState.matches(abi)) {
return primaryZygoteState;
}
@@ -661,10 +702,12 @@
// The primary zygote didn't match. Try the secondary.
if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) {
try {
- secondaryZygoteState = ZygoteState.connect(mSecondarySocket);
+ secondaryZygoteState =
+ ZygoteState.connect(mZygoteSecondarySocketAddress);
} catch (IOException ioe) {
throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe);
}
+
maybeSetApiBlacklistExemptions(secondaryZygoteState, false);
maybeSetHiddenApiAccessLogSampleRate(secondaryZygoteState);
}
@@ -685,11 +728,11 @@
IOException {
synchronized (mLock) {
ZygoteState state = openZygoteSocketIfNeeded(abi);
- state.writer.write("2");
- state.writer.newLine();
+ state.mZygoteOutputWriter.write("2");
+ state.mZygoteOutputWriter.newLine();
- state.writer.write("--preload-app");
- state.writer.newLine();
+ state.mZygoteOutputWriter.write("--preload-app");
+ state.mZygoteOutputWriter.newLine();
// Zygote args needs to be strings, so in order to pass ApplicationInfo,
// write it to a Parcel, and base64 the raw Parcel bytes to the other side.
@@ -697,12 +740,12 @@
appInfo.writeToParcel(parcel, 0 /* flags */);
String encodedParcelData = Base64.getEncoder().encodeToString(parcel.marshall());
parcel.recycle();
- state.writer.write(encodedParcelData);
- state.writer.newLine();
+ state.mZygoteOutputWriter.write(encodedParcelData);
+ state.mZygoteOutputWriter.newLine();
- state.writer.flush();
+ state.mZygoteOutputWriter.flush();
- return (state.inputStream.readInt() == 0);
+ return (state.mZygoteInputStream.readInt() == 0);
}
}
@@ -715,27 +758,27 @@
IOException {
synchronized(mLock) {
ZygoteState state = openZygoteSocketIfNeeded(abi);
- state.writer.write("5");
- state.writer.newLine();
+ state.mZygoteOutputWriter.write("5");
+ state.mZygoteOutputWriter.newLine();
- state.writer.write("--preload-package");
- state.writer.newLine();
+ state.mZygoteOutputWriter.write("--preload-package");
+ state.mZygoteOutputWriter.newLine();
- state.writer.write(packagePath);
- state.writer.newLine();
+ state.mZygoteOutputWriter.write(packagePath);
+ state.mZygoteOutputWriter.newLine();
- state.writer.write(libsPath);
- state.writer.newLine();
+ state.mZygoteOutputWriter.write(libsPath);
+ state.mZygoteOutputWriter.newLine();
- state.writer.write(libFileName);
- state.writer.newLine();
+ state.mZygoteOutputWriter.write(libFileName);
+ state.mZygoteOutputWriter.newLine();
- state.writer.write(cacheKey);
- state.writer.newLine();
+ state.mZygoteOutputWriter.write(cacheKey);
+ state.mZygoteOutputWriter.newLine();
- state.writer.flush();
+ state.mZygoteOutputWriter.flush();
- return (state.inputStream.readInt() == 0);
+ return (state.mZygoteInputStream.readInt() == 0);
}
}
@@ -749,13 +792,13 @@
synchronized (mLock) {
ZygoteState state = openZygoteSocketIfNeeded(abi);
// Each query starts with the argument count (1 in this case)
- state.writer.write("1");
- state.writer.newLine();
- state.writer.write("--preload-default");
- state.writer.newLine();
- state.writer.flush();
+ state.mZygoteOutputWriter.write("1");
+ state.mZygoteOutputWriter.newLine();
+ state.mZygoteOutputWriter.write("--preload-default");
+ state.mZygoteOutputWriter.newLine();
+ state.mZygoteOutputWriter.flush();
- return (state.inputStream.readInt() == 0);
+ return (state.mZygoteInputStream.readInt() == 0);
}
}
@@ -763,20 +806,21 @@
* Try connecting to the Zygote over and over again until we hit a time-out.
* @param socketName The name of the socket to connect to.
*/
- public static void waitForConnectionToZygote(String socketName) {
- final LocalSocketAddress address =
- new LocalSocketAddress(socketName, LocalSocketAddress.Namespace.RESERVED);
- waitForConnectionToZygote(address);
+ public static void waitForConnectionToZygote(String zygoteSocketName) {
+ final LocalSocketAddress zygoteSocketAddress =
+ new LocalSocketAddress(zygoteSocketName, LocalSocketAddress.Namespace.RESERVED);
+ waitForConnectionToZygote(zygoteSocketAddress);
}
/**
* Try connecting to the Zygote over and over again until we hit a time-out.
* @param address The name of the socket to connect to.
*/
- public static void waitForConnectionToZygote(LocalSocketAddress address) {
+ public static void waitForConnectionToZygote(LocalSocketAddress zygoteSocketAddress) {
for (int n = 20; n >= 0; n--) {
try {
- final ZygoteState zs = ZygoteState.connect(address);
+ final ZygoteState zs =
+ ZygoteState.connect(zygoteSocketAddress);
zs.close();
return;
} catch (IOException ioe) {
@@ -789,7 +833,8 @@
} catch (InterruptedException ie) {
}
}
- Slog.wtf(LOG_TAG, "Failed to connect to Zygote through socket " + address.getName());
+ Slog.wtf(LOG_TAG, "Failed to connect to Zygote through socket "
+ + zygoteSocketAddress.getName());
}
/**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 643c473..fa3f54a 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -11506,6 +11506,20 @@
public static final String DISPLAY_PANEL_LPM = "display_panel_lpm";
/**
+ * App time limit usage source setting.
+ * This controls which app in a task will be considered the source of usage when
+ * calculating app usage time limits.
+ *
+ * 1 -> task root app
+ * 2 -> current app
+ * Any other value defaults to task root app.
+ *
+ * Need to reboot the device for this setting to take effect.
+ * @hide
+ */
+ public static final String APP_TIME_LIMIT_USAGE_SOURCE = "app_time_limit_usage_source";
+
+ /**
* App standby (app idle) specific settings.
* This is encoded as a key=value list, separated by commas. Ex:
* <p>
@@ -11867,6 +11881,28 @@
public static final String KEEP_PROFILE_IN_BACKGROUND = "keep_profile_in_background";
/**
+ * The default time in ms within which a subsequent connection from an always allowed system
+ * is allowed to reconnect without user interaction.
+ *
+ * @hide
+ */
+ public static final long DEFAULT_ADB_ALLOWED_CONNECTION_TIME = 604800000;
+
+ /**
+ * When the user first connects their device to a system a prompt is displayed to allow
+ * the adb connection with an option to 'Always allow' connections from this system. If the
+ * user selects this always allow option then the connection time is stored for the system.
+ * This setting is the time in ms within which a subsequent connection from an always
+ * allowed system is allowed to reconnect without user interaction.
+ *
+ * Type: long
+ *
+ * @hide
+ */
+ public static final String ADB_ALLOWED_CONNECTION_TIME =
+ "adb_allowed_connection_time";
+
+ /**
* Get the key that retrieves a bluetooth headset's priority.
* @hide
*/
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 34d076f..47b206ca 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -20,6 +20,7 @@
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.HardwareRenderer;
+import android.graphics.Picture;
import android.graphics.Point;
import android.graphics.RecordingCanvas;
import android.graphics.Rect;
@@ -553,6 +554,10 @@
dumpProfileInfo(fd, flags);
}
+ Picture captureRenderingCommands() {
+ return null;
+ }
+
@Override
public boolean loadSystemProperties() {
boolean changed = super.loadSystemProperties();
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 292e933..5afc07f 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -17,17 +17,21 @@
package android.view;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.graphics.HardwareRenderer;
import android.graphics.Picture;
import android.graphics.RecordingCanvas;
import android.graphics.Rect;
import android.graphics.RenderNode;
import android.os.Debug;
import android.os.Handler;
+import android.os.Looper;
import android.os.RemoteException;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -48,16 +52,20 @@
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
+import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.function.Function;
/**
* Various debugging/tracing tools related to {@link View} and the view hierarchy.
@@ -741,6 +749,123 @@
root.getViewRootImpl().outputDisplayList(target);
}
+ private static class PictureCallbackHandler implements AutoCloseable,
+ HardwareRenderer.PictureCapturedCallback, Runnable {
+ private final HardwareRenderer mRenderer;
+ private final Function<Picture, Boolean> mCallback;
+ private final Executor mExecutor;
+ private final ReentrantLock mLock = new ReentrantLock(false);
+ private final ArrayDeque<Picture> mQueue = new ArrayDeque<>(3);
+ private boolean mStopListening;
+ private Thread mRenderThread;
+
+ private PictureCallbackHandler(HardwareRenderer renderer,
+ Function<Picture, Boolean> callback, Executor executor) {
+ mRenderer = renderer;
+ mCallback = callback;
+ mExecutor = executor;
+ mRenderer.setPictureCaptureCallback(this);
+ }
+
+ @Override
+ public void close() {
+ mLock.lock();
+ mStopListening = true;
+ mLock.unlock();
+ mRenderer.setPictureCaptureCallback(null);
+ }
+
+ @Override
+ public void onPictureCaptured(Picture picture) {
+ mLock.lock();
+ if (mStopListening) {
+ mLock.unlock();
+ mRenderer.setPictureCaptureCallback(null);
+ return;
+ }
+ if (mRenderThread == null) {
+ mRenderThread = Thread.currentThread();
+ }
+ Picture toDestroy = null;
+ if (mQueue.size() == 3) {
+ toDestroy = mQueue.removeLast();
+ }
+ mQueue.add(picture);
+ mLock.unlock();
+ if (toDestroy == null) {
+ mExecutor.execute(this);
+ } else {
+ toDestroy.close();
+ }
+ }
+
+ @Override
+ public void run() {
+ mLock.lock();
+ final Picture picture = mQueue.poll();
+ final boolean isStopped = mStopListening;
+ mLock.unlock();
+ if (Thread.currentThread() == mRenderThread) {
+ close();
+ throw new IllegalStateException(
+ "ViewDebug#startRenderingCommandsCapture must be given an executor that "
+ + "invokes asynchronously");
+ }
+ if (isStopped) {
+ picture.close();
+ return;
+ }
+ final boolean keepReceiving = mCallback.apply(picture);
+ if (!keepReceiving) {
+ close();
+ }
+ }
+ }
+
+ /**
+ * Begins capturing the entire rendering commands for the view tree referenced by the given
+ * view. The view passed may be any View in the tree as long as it is attached. That is,
+ * {@link View#isAttachedToWindow()} must be true.
+ *
+ * Every time a frame is rendered a Picture will be passed to the given callback via the given
+ * executor. As long as the callback returns 'true' it will continue to receive new frames.
+ * The system will only invoke the callback at a rate that the callback is able to keep up with.
+ * That is, if it takes 48ms for the callback to complete and there is a 60fps animation running
+ * then the callback will only receive 33% of the frames produced.
+ *
+ * This method must be called on the same thread as the View tree.
+ *
+ * @param tree The View tree to capture the rendering commands.
+ * @param callback The callback to invoke on every frame produced. Should return true to
+ * continue receiving new frames, false to stop capturing.
+ * @param executor The executor to invoke the callback on. Recommend using a background thread
+ * to avoid stalling the UI thread. Must be an asynchronous invoke or an
+ * exception will be thrown.
+ * @return a closeable that can be used to stop capturing. May be invoked on any thread. Note
+ * that the callback may continue to receive another frame or two depending on thread timings.
+ * Returns null if the capture stream cannot be started, such as if there's no
+ * HardwareRenderer for the given view tree.
+ * @hide
+ */
+ @TestApi
+ @Nullable
+ public static AutoCloseable startRenderingCommandsCapture(View tree, Executor executor,
+ Function<Picture, Boolean> callback) {
+ final View.AttachInfo attachInfo = tree.mAttachInfo;
+ if (attachInfo == null) {
+ throw new IllegalArgumentException("Given view isn't attached");
+ }
+ if (attachInfo.mHandler.getLooper() != Looper.myLooper()) {
+ throw new IllegalStateException("Called on the wrong thread."
+ + " Must be called on the thread that owns the given View");
+ }
+ final HardwareRenderer renderer = attachInfo.mThreadedRenderer;
+ if (renderer != null) {
+ return new PictureCallbackHandler(renderer, callback, executor);
+ }
+ return null;
+ }
+
private static void capture(View root, final OutputStream clientStream, String parameter)
throws IOException {
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 10b99ec..15088d2 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -27,6 +27,7 @@
import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.app.ActivityThread;
+import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -1881,7 +1882,15 @@
/**
* Notify the event when the user tapped or clicked the text view.
+ *
+ * @param view {@link View} which is being clicked.
+ * @see InputMethodService#onViewClicked(boolean)
+ * @deprecated The semantics of this method can never be defined well for composite {@link View}
+ * that works as a giant "Canvas", which can host its own UI hierarchy and sub focus
+ * state. {@link android.webkit.WebView} is a good example. Application / IME
+ * developers should not rely on this method.
*/
+ @Deprecated
public void viewClicked(View view) {
// Re-dispatch if there is a context mismatch.
final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(view);
@@ -2484,11 +2493,48 @@
*/
@RequiresPermission(WRITE_SECURE_SETTINGS)
public boolean setCurrentInputMethodSubtype(InputMethodSubtype subtype) {
- try {
- return mService.setCurrentInputMethodSubtype(subtype);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ if (Process.myUid() == Process.SYSTEM_UID) {
+ Log.w(TAG, "System process should not call setCurrentInputMethodSubtype() because "
+ + "almost always it is a bug under multi-user / multi-profile environment. "
+ + "Consider directly interacting with InputMethodManagerService "
+ + "via LocalServices.");
+ return false;
}
+ if (subtype == null) {
+ // See the JavaDoc. This is how this method has worked.
+ return false;
+ }
+ final Context fallbackContext = ActivityThread.currentApplication();
+ if (fallbackContext == null) {
+ return false;
+ }
+ if (fallbackContext.checkSelfPermission(WRITE_SECURE_SETTINGS)
+ != PackageManager.PERMISSION_GRANTED) {
+ return false;
+ }
+ final ContentResolver contentResolver = fallbackContext.getContentResolver();
+ final String imeId = Settings.Secure.getString(contentResolver,
+ Settings.Secure.DEFAULT_INPUT_METHOD);
+ if (ComponentName.unflattenFromString(imeId) == null) {
+ // Null or invalid IME ID format.
+ return false;
+ }
+ final List<InputMethodSubtype> enabledSubtypes;
+ try {
+ enabledSubtypes = mService.getEnabledInputMethodSubtypeList(imeId, true);
+ } catch (RemoteException e) {
+ return false;
+ }
+ final int numSubtypes = enabledSubtypes.size();
+ for (int i = 0; i < numSubtypes; ++i) {
+ final InputMethodSubtype enabledSubtype = enabledSubtypes.get(i);
+ if (enabledSubtype.equals(subtype)) {
+ Settings.Secure.putInt(contentResolver,
+ Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE, enabledSubtype.hashCode());
+ return true;
+ }
+ }
+ return false;
}
/**
diff --git a/core/java/android/webkit/WebViewZygote.java b/core/java/android/webkit/WebViewZygote.java
index 29b3b3c..3a1c457 100644
--- a/core/java/android/webkit/WebViewZygote.java
+++ b/core/java/android/webkit/WebViewZygote.java
@@ -150,7 +150,7 @@
}
try {
- sZygote = Process.zygoteProcess.startChildZygote(
+ sZygote = Process.ZYGOTE_PROCESS.startChildZygote(
"com.android.internal.os.WebViewZygoteInit",
"webview_zygote",
Process.WEBVIEW_ZYGOTE_UID,
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index b28f4cd..f609f2f 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -16,13 +16,33 @@
package com.android.internal.os;
+import static android.system.OsConstants.O_CLOEXEC;
+
+import static com.android.internal.os.ZygoteConnectionConstants.MAX_ZYGOTE_ARGC;
+
+import android.net.Credentials;
+import android.net.LocalServerSocket;
+import android.net.LocalSocket;
+import android.os.FactoryTest;
import android.os.IVold;
+import android.os.Process;
+import android.os.SystemProperties;
import android.os.Trace;
import android.system.ErrnoException;
import android.system.Os;
+import android.util.Log;
import dalvik.system.ZygoteHooks;
+import libcore.io.IoUtils;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
/** @hide */
public final class Zygote {
/*
@@ -94,6 +114,18 @@
/** Read-write external storage should be mounted instead of package sandbox */
public static final int MOUNT_EXTERNAL_FULL = IVold.REMOUNT_MODE_FULL;
+ /** Number of bytes sent to the Zygote over blastula pipes or the pool event FD */
+ public static final int BLASTULA_MANAGEMENT_MESSAGE_BYTES = 8;
+
+ /** If the blastula pool should be created and used to start applications */
+ public static final boolean BLASTULA_POOL_ENABLED = false;
+
+ /**
+ * File descriptor used for communication between the signal handler and the ZygoteServer poll
+ * loop.
+ * */
+ protected static FileDescriptor sBlastulaPoolEventFD;
+
private static final ZygoteHooks VM_HOOKS = new ZygoteHooks();
/**
@@ -123,6 +155,43 @@
*/
public static final String CHILD_ZYGOTE_UID_RANGE_END = "--uid-range-end=";
+ /** Prefix prepended to socket names created by init */
+ private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_";
+
+ /**
+ * The maximum value that the sBlastulaPoolMax variable may take. This value
+ * is a mirror of BLASTULA_POOL_MAX_LIMIT found in com_android_internal_os_Zygote.cpp.
+ */
+ static final int BLASTULA_POOL_MAX_LIMIT = 10;
+
+ /**
+ * The minimum value that the sBlastulaPoolMin variable may take.
+ */
+ static final int BLASTULA_POOL_MIN_LIMIT = 1;
+
+ /**
+ * The runtime-adjustable maximum Blastula pool size.
+ */
+ static int sBlastulaPoolMax = BLASTULA_POOL_MAX_LIMIT;
+
+ /**
+ * The runtime-adjustable minimum Blastula pool size.
+ */
+ static int sBlastulaPoolMin = BLASTULA_POOL_MIN_LIMIT;
+
+ /**
+ * The runtime-adjustable value used to determine when to re-fill the
+ * blastula pool. The pool will be re-filled when
+ * (sBlastulaPoolMax - gBlastulaPoolCount) >= sBlastulaPoolRefillThreshold.
+ */
+ // TODO (chriswailes): This must be updated at the same time as sBlastulaPoolMax.
+ static int sBlastulaPoolRefillThreshold = (sBlastulaPoolMax / 2);
+
+ private static LocalServerSocket sBlastulaPoolSocket = null;
+
+ /** a prototype instance for a future List.toArray() */
+ protected static final int[][] INT_ARRAY_2D = new int[0][0];
+
private Zygote() {}
/** Called for some security initialization before any fork. */
@@ -189,6 +258,51 @@
int[] fdsToClose, int[] fdsToIgnore, boolean startChildZygote, String instructionSet,
String appDataDir, String packageName, String[] packagesForUid, String[] visibleVolIds);
+ /**
+ * Specialize a Blastula instance. The current VM must have been started
+ * with the -Xzygote flag.
+ *
+ * @param uid The UNIX uid that the new process should setuid() to before spawning any threads
+ * @param gid The UNIX gid that the new process should setgid() to before spawning any threads
+ * @param gids null-ok; A list of UNIX gids that the new process should
+ * setgroups() to before spawning any threads
+ * @param runtimeFlags Bit flags that enable ART features
+ * @param rlimits null-ok An array of rlimit tuples, with the second
+ * dimension having a length of 3 and representing
+ * (resource, rlim_cur, rlim_max). These are set via the posix
+ * setrlimit(2) call.
+ * @param seInfo null-ok A string specifying SELinux information for
+ * the new process.
+ * @param niceName null-ok A string specifying the process name.
+ * @param startChildZygote If true, the new child process will itself be a
+ * new zygote process.
+ * @param instructionSet null-ok The instruction set to use.
+ * @param appDataDir null-ok The data directory of the app.
+ */
+ public static void specializeBlastula(int uid, int gid, int[] gids, int runtimeFlags,
+ int[][] rlimits, int mountExternal, String seInfo, String niceName,
+ boolean startChildZygote, String instructionSet, String appDataDir,
+ String packageName, String[] packagesForUid, String[] visibleVolIds) {
+
+ nativeSpecializeBlastula(uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo,
+ niceName, startChildZygote, instructionSet, appDataDir,
+ packageName, packagesForUid, visibleVolIds);
+
+ // Enable tracing as soon as possible for the child process.
+ Trace.setTracingEnabled(true, runtimeFlags);
+
+ // Note that this event ends at the end of handleChildProc.
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork");
+
+ /*
+ * This is called here (instead of after the fork but before the specialize) to maintain
+ * consistancy with the code paths for forkAndSpecialize.
+ *
+ * TODO (chriswailes): Look into moving this to immediately after the fork.
+ */
+ VM_HOOKS.postForkCommon();
+ }
+
private static native void nativeSpecializeBlastula(int uid, int gid, int[] gids,
int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName,
boolean startChildZygote, String instructionSet, String appDataDir, String packageName,
@@ -259,20 +373,483 @@
*/
protected static native void nativeUnmountStorageOnInit();
+ /**
+ * Get socket file descriptors (opened by init) from the environment and
+ * store them for access from native code later.
+ *
+ * @param isPrimary True if this is the zygote process, false if it is zygote_secondary
+ */
+ public static void getSocketFDs(boolean isPrimary) {
+ nativeGetSocketFDs(isPrimary);
+ }
+
protected static native void nativeGetSocketFDs(boolean isPrimary);
+ /**
+ * Initialize the blastula pool and fill it with the desired number of
+ * processes.
+ */
+ protected static Runnable initBlastulaPool() {
+ if (BLASTULA_POOL_ENABLED) {
+ sBlastulaPoolEventFD = getBlastulaPoolEventFD();
+
+ return fillBlastulaPool(null);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Checks to see if the current policy says that pool should be refilled, and spawns new
+ * blastulas if necessary.
+ *
+ * NOTE: This function doesn't need to be guarded with BLASTULA_POOL_ENABLED because it is
+ * only called from contexts that are only valid if the pool is enabled.
+ *
+ * @param sessionSocketRawFDs Anonymous session sockets that are currently open
+ * @return In the Zygote process this function will always return null; in blastula processes
+ * this function will return a Runnable object representing the new application that is
+ * passed up from blastulaMain.
+ */
+ protected static Runnable fillBlastulaPool(int[] sessionSocketRawFDs) {
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Zygote:FillBlastulaPool");
+
+ int blastulaPoolCount = getBlastulaPoolCount();
+
+ int numBlastulasToSpawn = sBlastulaPoolMax - blastulaPoolCount;
+
+ if (blastulaPoolCount < sBlastulaPoolMin
+ || numBlastulasToSpawn >= sBlastulaPoolRefillThreshold) {
+
+ // Disable some VM functionality and reset some system values
+ // before forking.
+ VM_HOOKS.preFork();
+ resetNicePriority();
+
+ while (blastulaPoolCount++ < sBlastulaPoolMax) {
+ Runnable caller = forkBlastula(sessionSocketRawFDs);
+
+ if (caller != null) {
+ return caller;
+ }
+ }
+
+ // Re-enable runtime services for the Zygote. Blastula services
+ // are re-enabled in specializeBlastula.
+ VM_HOOKS.postForkCommon();
+
+ Log.i("zygote", "Filled the blastula pool. New blastulas: " + numBlastulasToSpawn);
+ }
+
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+
+ return null;
+ }
+
+ /**
+ * @return Number of blastulas currently in the pool
+ */
+ private static int getBlastulaPoolCount() {
+ return nativeGetBlastulaPoolCount();
+ }
+
private static native int nativeGetBlastulaPoolCount();
+ /**
+ * @return The event FD used for communication between the signal handler and the ZygoteServer
+ * poll loop
+ */
+ private static FileDescriptor getBlastulaPoolEventFD() {
+ FileDescriptor fd = new FileDescriptor();
+ fd.setInt$(nativeGetBlastulaPoolEventFD());
+
+ return fd;
+ }
+
private static native int nativeGetBlastulaPoolEventFD();
+ /**
+ * Fork a new blastula process from the zygote
+ *
+ * @param sessionSocketRawFDs Anonymous session sockets that are currently open
+ * @return In the Zygote process this function will always return null; in blastula processes
+ * this function will return a Runnable object representing the new application that is
+ * passed up from blastulaMain.
+ */
+ private static Runnable forkBlastula(int[] sessionSocketRawFDs) {
+ FileDescriptor[] pipeFDs = null;
+
+ try {
+ pipeFDs = Os.pipe2(O_CLOEXEC);
+ } catch (ErrnoException errnoEx) {
+ throw new IllegalStateException("Unable to create blastula pipe.", errnoEx);
+ }
+
+ int pid =
+ nativeForkBlastula(pipeFDs[0].getInt$(), pipeFDs[1].getInt$(), sessionSocketRawFDs);
+
+ if (pid == 0) {
+ IoUtils.closeQuietly(pipeFDs[0]);
+ return blastulaMain(pipeFDs[1]);
+ } else {
+ // The read-end of the pipe will be closed by the native code.
+ // See removeBlastulaTableEntry();
+ IoUtils.closeQuietly(pipeFDs[1]);
+ return null;
+ }
+ }
+
private static native int nativeForkBlastula(int readPipeFD,
int writePipeFD,
int[] sessionSocketRawFDs);
+ /**
+ * This function is used by blastulas to wait for specialization requests from the system
+ * server.
+ *
+ * @param writePipe The write end of the reporting pipe used to communicate with the poll loop
+ * of the ZygoteServer.
+ * @return A runnable oject representing the new application.
+ */
+ static Runnable blastulaMain(FileDescriptor writePipe) {
+ final int pid = Process.myPid();
+
+ LocalSocket sessionSocket = null;
+ DataOutputStream blastulaOutputStream = null;
+ Credentials peerCredentials = null;
+ String[] argStrings = null;
+
+ while (true) {
+ try {
+ sessionSocket = sBlastulaPoolSocket.accept();
+
+ BufferedReader blastulaReader =
+ new BufferedReader(new InputStreamReader(sessionSocket.getInputStream()));
+ blastulaOutputStream =
+ new DataOutputStream(sessionSocket.getOutputStream());
+
+ peerCredentials = sessionSocket.getPeerCredentials();
+
+ argStrings = readArgumentList(blastulaReader);
+
+ if (argStrings != null) {
+ break;
+ } else {
+ Log.e("Blastula", "Truncated command received.");
+ IoUtils.closeQuietly(sessionSocket);
+ }
+ } catch (IOException ioEx) {
+ Log.e("Blastula", "Failed to read command: " + ioEx.getMessage());
+ IoUtils.closeQuietly(sessionSocket);
+ }
+ }
+
+ ZygoteArguments args = new ZygoteArguments(argStrings);
+
+ // TODO (chriswailes): Should this only be run for debug builds?
+ validateBlastulaCommand(args);
+
+ applyUidSecurityPolicy(args, peerCredentials);
+ applyDebuggerSystemProperty(args);
+
+ int[][] rlimits = null;
+
+ if (args.mRLimits != null) {
+ rlimits = args.mRLimits.toArray(INT_ARRAY_2D);
+ }
+
+ // This must happen before the SELinux policy for this process is
+ // changed when specializing.
+ try {
+ // Used by ZygoteProcess.zygoteSendArgsAndGetResult to fill in a
+ // Process.ProcessStartResult object.
+ blastulaOutputStream.writeInt(pid);
+ } catch (IOException ioEx) {
+ Log.e("Blastula", "Failed to write response to session socket: " + ioEx.getMessage());
+ System.exit(-1);
+ } finally {
+ IoUtils.closeQuietly(sessionSocket);
+ IoUtils.closeQuietly(sBlastulaPoolSocket);
+ }
+
+ try {
+ ByteArrayOutputStream buffer =
+ new ByteArrayOutputStream(Zygote.BLASTULA_MANAGEMENT_MESSAGE_BYTES);
+ DataOutputStream outputStream = new DataOutputStream(buffer);
+
+ // This is written as a long so that the blastula reporting pipe and blastula pool
+ // event FD handlers in ZygoteServer.runSelectLoop can be unified. These two cases
+ // should both send/receive 8 bytes.
+ outputStream.writeLong(pid);
+ outputStream.flush();
+
+ Os.write(writePipe, buffer.toByteArray(), 0, buffer.size());
+ } catch (Exception ex) {
+ Log.e("Blastula",
+ String.format("Failed to write PID (%d) to pipe (%d): %s",
+ pid, writePipe.getInt$(), ex.getMessage()));
+ System.exit(-1);
+ } finally {
+ IoUtils.closeQuietly(writePipe);
+ }
+
+ specializeBlastula(args.mUid, args.mGid, args.mGids,
+ args.mRuntimeFlags, rlimits, args.mMountExternal,
+ args.mSeInfo, args.mNiceName, args.mStartChildZygote,
+ args.mInstructionSet, args.mAppDataDir, args.mPackageName,
+ args.mPackagesForUid, args.mVisibleVolIds);
+
+ if (args.mNiceName != null) {
+ Process.setArgV0(args.mNiceName);
+ }
+
+ // End of the postFork event.
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+
+ return ZygoteInit.zygoteInit(args.mTargetSdkVersion,
+ args.mRemainingArgs,
+ null /* classLoader */);
+ }
+
+ private static final String BLASTULA_ERROR_PREFIX = "Invalid command to blastula: ";
+
+ /**
+ * Checks a set of zygote arguments to see if they can be handled by a blastula. Throws an
+ * exception if an invalid arugment is encountered.
+ * @param args The arguments to test
+ */
+ static void validateBlastulaCommand(ZygoteArguments args) {
+ if (args.mAbiListQuery) {
+ throw new IllegalArgumentException(BLASTULA_ERROR_PREFIX + "--query-abi-list");
+ } else if (args.mPidQuery) {
+ throw new IllegalArgumentException(BLASTULA_ERROR_PREFIX + "--get-pid");
+ } else if (args.mPreloadDefault) {
+ throw new IllegalArgumentException(BLASTULA_ERROR_PREFIX + "--preload-default");
+ } else if (args.mPreloadPackage != null) {
+ throw new IllegalArgumentException(BLASTULA_ERROR_PREFIX + "--preload-package");
+ } else if (args.mPreloadApp != null) {
+ throw new IllegalArgumentException(BLASTULA_ERROR_PREFIX + "--preload-app");
+ } else if (args.mStartChildZygote) {
+ throw new IllegalArgumentException(BLASTULA_ERROR_PREFIX + "--start-child-zygote");
+ } else if (args.mApiBlacklistExemptions != null) {
+ throw new IllegalArgumentException(
+ BLASTULA_ERROR_PREFIX + "--set-api-blacklist-exemptions");
+ } else if (args.mHiddenApiAccessLogSampleRate != -1) {
+ throw new IllegalArgumentException(
+ BLASTULA_ERROR_PREFIX + "--hidden-api-log-sampling-rate=");
+ } else if (args.mInvokeWith != null) {
+ throw new IllegalArgumentException(BLASTULA_ERROR_PREFIX + "--invoke-with");
+ } else if (args.mPermittedCapabilities != 0 || args.mEffectiveCapabilities != 0) {
+ throw new ZygoteSecurityException("Client may not specify capabilities: "
+ + "permitted=0x" + Long.toHexString(args.mPermittedCapabilities)
+ + ", effective=0x" + Long.toHexString(args.mEffectiveCapabilities));
+ }
+ }
+
+ /**
+ * @return Raw file descriptors for the read-end of blastula reporting pipes.
+ */
+ protected static int[] getBlastulaPipeFDs() {
+ return nativeGetBlastulaPipeFDs();
+ }
+
private static native int[] nativeGetBlastulaPipeFDs();
+ /**
+ * Remove the blastula table entry for the provided process ID.
+ *
+ * @param blastulaPID Process ID of the entry to remove
+ * @return True if the entry was removed; false if it doesn't exist
+ */
+ protected static boolean removeBlastulaTableEntry(int blastulaPID) {
+ return nativeRemoveBlastulaTableEntry(blastulaPID);
+ }
+
private static native boolean nativeRemoveBlastulaTableEntry(int blastulaPID);
+ /**
+ * uid 1000 (Process.SYSTEM_UID) may specify any uid > 1000 in normal
+ * operation. It may also specify any gid and setgroups() list it chooses.
+ * In factory test mode, it may specify any UID.
+ *
+ * @param args non-null; zygote spawner arguments
+ * @param peer non-null; peer credentials
+ * @throws ZygoteSecurityException
+ */
+ protected static void applyUidSecurityPolicy(ZygoteArguments args, Credentials peer)
+ throws ZygoteSecurityException {
+
+ if (peer.getUid() == Process.SYSTEM_UID) {
+ /* In normal operation, SYSTEM_UID can only specify a restricted
+ * set of UIDs. In factory test mode, SYSTEM_UID may specify any uid.
+ */
+ boolean uidRestricted = FactoryTest.getMode() == FactoryTest.FACTORY_TEST_OFF;
+
+ if (uidRestricted && args.mUidSpecified && (args.mUid < Process.SYSTEM_UID)) {
+ throw new ZygoteSecurityException(
+ "System UID may not launch process with UID < "
+ + Process.SYSTEM_UID);
+ }
+ }
+
+ // If not otherwise specified, uid and gid are inherited from peer
+ if (!args.mUidSpecified) {
+ args.mUid = peer.getUid();
+ args.mUidSpecified = true;
+ }
+ if (!args.mGidSpecified) {
+ args.mGid = peer.getGid();
+ args.mGidSpecified = true;
+ }
+ }
+
+ /**
+ * Applies debugger system properties to the zygote arguments.
+ *
+ * If "ro.debuggable" is "1", all apps are debuggable. Otherwise,
+ * the debugger state is specified via the "--enable-jdwp" flag
+ * in the spawn request.
+ *
+ * @param args non-null; zygote spawner args
+ */
+ protected static void applyDebuggerSystemProperty(ZygoteArguments args) {
+ if (RoSystemProperties.DEBUGGABLE) {
+ args.mRuntimeFlags |= Zygote.DEBUG_ENABLE_JDWP;
+ }
+ }
+
+ /**
+ * Applies zygote security policy.
+ * Based on the credentials of the process issuing a zygote command:
+ * <ol>
+ * <li> uid 0 (root) may specify --invoke-with to launch Zygote with a
+ * wrapper command.
+ * <li> Any other uid may not specify any invoke-with argument.
+ * </ul>
+ *
+ * @param args non-null; zygote spawner arguments
+ * @param peer non-null; peer credentials
+ * @throws ZygoteSecurityException
+ */
+ protected static void applyInvokeWithSecurityPolicy(ZygoteArguments args, Credentials peer)
+ throws ZygoteSecurityException {
+ int peerUid = peer.getUid();
+
+ if (args.mInvokeWith != null && peerUid != 0
+ && (args.mRuntimeFlags & Zygote.DEBUG_ENABLE_JDWP) == 0) {
+ throw new ZygoteSecurityException("Peer is permitted to specify an"
+ + "explicit invoke-with wrapper command only for debuggable"
+ + "applications.");
+ }
+ }
+
+ /**
+ * Applies invoke-with system properties to the zygote arguments.
+ *
+ * @param args non-null; zygote args
+ */
+ protected static void applyInvokeWithSystemProperty(ZygoteArguments args) {
+ if (args.mInvokeWith == null && args.mNiceName != null) {
+ String property = "wrap." + args.mNiceName;
+ args.mInvokeWith = SystemProperties.get(property);
+ if (args.mInvokeWith != null && args.mInvokeWith.length() == 0) {
+ args.mInvokeWith = null;
+ }
+ }
+ }
+
+ /**
+ * Reads an argument list from the provided socket
+ * @return Argument list or null if EOF is reached
+ * @throws IOException passed straight through
+ */
+ static String[] readArgumentList(BufferedReader socketReader) throws IOException {
+
+ /**
+ * See android.os.Process.zygoteSendArgsAndGetPid()
+ * Presently the wire format to the zygote process is:
+ * a) a count of arguments (argc, in essence)
+ * b) a number of newline-separated argument strings equal to count
+ *
+ * After the zygote process reads these it will write the pid of
+ * the child or -1 on failure.
+ */
+
+ int argc;
+
+ try {
+ String argc_string = socketReader.readLine();
+
+ if (argc_string == null) {
+ // EOF reached.
+ return null;
+ }
+ argc = Integer.parseInt(argc_string);
+
+ } catch (NumberFormatException ex) {
+ Log.e("Zygote", "Invalid Zygote wire format: non-int at argc");
+ throw new IOException("Invalid wire format");
+ }
+
+ // See bug 1092107: large argc can be used for a DOS attack
+ if (argc > MAX_ZYGOTE_ARGC) {
+ throw new IOException("Max arg count exceeded");
+ }
+
+ String[] args = new String[argc];
+ for (int arg_index = 0; arg_index < argc; arg_index++) {
+ args[arg_index] = socketReader.readLine();
+ if (args[arg_index] == null) {
+ // We got an unexpected EOF.
+ throw new IOException("Truncated request");
+ }
+ }
+
+ return args;
+ }
+
+ /**
+ * Creates a managed object representing the Blastula pool socket that has
+ * already been initialized and bound by init.
+ *
+ * TODO (chriswailes): Move the name selection logic into this function.
+ *
+ * @throws RuntimeException when open fails
+ */
+ static void createBlastulaSocket(String socketName) {
+ if (BLASTULA_POOL_ENABLED && sBlastulaPoolSocket == null) {
+ sBlastulaPoolSocket = createManagedSocketFromInitSocket(socketName);
+ }
+ }
+
+ /**
+ * Creates a managed LocalServerSocket object using a file descriptor
+ * created by an init.rc script. The init scripts that specify the
+ * sockets name can be found in system/core/rootdir. The socket is bound
+ * to the file system in the /dev/sockets/ directory, and the file
+ * descriptor is shared via the ANDROID_SOCKET_<socketName> environment
+ * variable.
+ */
+ static LocalServerSocket createManagedSocketFromInitSocket(String socketName) {
+ int fileDesc;
+ final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
+
+ try {
+ String env = System.getenv(fullSocketName);
+ fileDesc = Integer.parseInt(env);
+ } catch (RuntimeException ex) {
+ throw new RuntimeException("Socket unset or invalid: " + fullSocketName, ex);
+ }
+
+ try {
+ FileDescriptor fd = new FileDescriptor();
+ fd.setInt$(fileDesc);
+ return new LocalServerSocket(fd);
+ } catch (IOException ex) {
+ throw new RuntimeException(
+ "Error building socket from file descriptor: " + fileDesc, ex);
+ }
+ }
private static void callPostForkSystemServerHooks() {
// SystemServer specific post fork hooks run before child post fork hooks.
diff --git a/core/java/com/android/internal/os/ZygoteArguments.java b/core/java/com/android/internal/os/ZygoteArguments.java
new file mode 100644
index 0000000..df89b26
--- /dev/null
+++ b/core/java/com/android/internal/os/ZygoteArguments.java
@@ -0,0 +1,428 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * Handles argument parsing for args related to the zygote spawner.
+ *
+ * Current recognized args:
+ * <ul>
+ * <li> --setuid=<i>uid of child process, defaults to 0</i>
+ * <li> --setgid=<i>gid of child process, defaults to 0</i>
+ * <li> --setgroups=<i>comma-separated list of supplimentary gid's</i>
+ * <li> --capabilities=<i>a pair of comma-separated integer strings
+ * indicating Linux capabilities(2) set for child. The first string
+ * represents the <code>permitted</code> set, and the second the
+ * <code>effective</code> set. Precede each with 0 or
+ * 0x for octal or hexidecimal value. If unspecified, both default to 0.
+ * This parameter is only applied if the uid of the new process will
+ * be non-0. </i>
+ * <li> --rlimit=r,c,m<i>tuple of values for setrlimit() call.
+ * <code>r</code> is the resource, <code>c</code> and <code>m</code>
+ * are the settings for current and max value.</i>
+ * <li> --instruction-set=<i>instruction-set-string</i> which instruction set to use/emulate.
+ * <li> --nice-name=<i>nice name to appear in ps</i>
+ * <li> --package-name=<i>package name this process belongs to</i>
+ * <li> --runtime-args indicates that the remaining arg list should
+ * be handed off to com.android.internal.os.RuntimeInit, rather than
+ * processed directly.
+ * Android runtime startup (eg, Binder initialization) is also eschewed.
+ * <li> [--] <args for RuntimeInit >
+ * </ul>
+ */
+class ZygoteArguments {
+
+ /**
+ * from --setuid
+ */
+ int mUid = 0;
+ boolean mUidSpecified;
+
+ /**
+ * from --setgid
+ */
+ int mGid = 0;
+ boolean mGidSpecified;
+
+ /**
+ * from --setgroups
+ */
+ int[] mGids;
+
+ /**
+ * From --runtime-flags.
+ */
+ int mRuntimeFlags;
+
+ /**
+ * From --mount-external
+ */
+ int mMountExternal = Zygote.MOUNT_EXTERNAL_NONE;
+
+ /**
+ * from --target-sdk-version.
+ */
+ int mTargetSdkVersion;
+ boolean mTargetSdkVersionSpecified;
+
+ /**
+ * from --nice-name
+ */
+ String mNiceName;
+
+ /**
+ * from --capabilities
+ */
+ boolean mCapabilitiesSpecified;
+ long mPermittedCapabilities;
+ long mEffectiveCapabilities;
+
+ /**
+ * from --seinfo
+ */
+ boolean mSeInfoSpecified;
+ String mSeInfo;
+
+ /**
+ * from all --rlimit=r,c,m
+ */
+ ArrayList<int[]> mRLimits;
+
+ /**
+ * from --invoke-with
+ */
+ String mInvokeWith;
+
+ /** from --package-name */
+ String mPackageName;
+
+ /** from --packages-for-uid */
+ String[] mPackagesForUid;
+
+ /** from --visible-vols */
+ String[] mVisibleVolIds;
+
+ /**
+ * Any args after and including the first non-option arg (or after a '--')
+ */
+ String[] mRemainingArgs;
+
+ /**
+ * Whether the current arguments constitute an ABI list query.
+ */
+ boolean mAbiListQuery;
+
+ /**
+ * The instruction set to use, or null when not important.
+ */
+ String mInstructionSet;
+
+ /**
+ * The app data directory. May be null, e.g., for the system server. Note that this might not be
+ * reliable in the case of process-sharing apps.
+ */
+ String mAppDataDir;
+
+ /**
+ * The APK path of the package to preload, when using --preload-package.
+ */
+ String mPreloadPackage;
+
+ /**
+ * A Base64 string representing a serialize ApplicationInfo Parcel,
+ when using --preload-app.
+ */
+ String mPreloadApp;
+
+ /**
+ * The native library path of the package to preload, when using --preload-package.
+ */
+ String mPreloadPackageLibs;
+
+ /**
+ * The filename of the native library to preload, when using --preload-package.
+ */
+ String mPreloadPackageLibFileName;
+
+ /**
+ * The cache key under which to enter the preloaded package into the classloader cache, when
+ * using --preload-package.
+ */
+ String mPreloadPackageCacheKey;
+
+ /**
+ * Whether this is a request to start preloading the default resources and classes. This
+ * argument only makes sense when the zygote is in lazy preload mode (i.e, when it's started
+ * with --enable-lazy-preload).
+ */
+ boolean mPreloadDefault;
+
+ /**
+ * Whether this is a request to start a zygote process as a child of this zygote. Set with
+ * --start-child-zygote. The remaining arguments must include the CHILD_ZYGOTE_SOCKET_NAME_ARG
+ * flag to indicate the abstract socket name that should be used for communication.
+ */
+ boolean mStartChildZygote;
+
+ /**
+ * Whether the current arguments constitute a request for the zygote's PID.
+ */
+ boolean mPidQuery;
+
+ /**
+ * Exemptions from API blacklisting. These are sent to the pre-forked zygote at boot time, or
+ * when they change, via --set-api-blacklist-exemptions.
+ */
+ String[] mApiBlacklistExemptions;
+
+ /**
+ * Sampling rate for logging hidden API accesses to the event log. This is sent to the
+ * pre-forked zygote at boot time, or when it changes, via --hidden-api-log-sampling-rate.
+ */
+ int mHiddenApiAccessLogSampleRate = -1;
+
+ /**
+ * Constructs instance and parses args
+ *
+ * @param args zygote command-line args
+ */
+ ZygoteArguments(String[] args) throws IllegalArgumentException {
+ parseArgs(args);
+ }
+
+ /**
+ * Parses the commandline arguments intended for the Zygote spawner (such as "--setuid=" and
+ * "--setgid=") and creates an array containing the remaining args.
+ *
+ * Per security review bug #1112214, duplicate args are disallowed in critical cases to make
+ * injection harder.
+ */
+ private void parseArgs(String[] args)
+ throws IllegalArgumentException {
+ int curArg = 0;
+
+ boolean seenRuntimeArgs = false;
+
+ boolean expectRuntimeArgs = true;
+ for ( /* curArg */ ; curArg < args.length; curArg++) {
+ String arg = args[curArg];
+
+ if (arg.equals("--")) {
+ curArg++;
+ break;
+ } else if (arg.startsWith("--setuid=")) {
+ if (mUidSpecified) {
+ throw new IllegalArgumentException(
+ "Duplicate arg specified");
+ }
+ mUidSpecified = true;
+ mUid = Integer.parseInt(
+ arg.substring(arg.indexOf('=') + 1));
+ } else if (arg.startsWith("--setgid=")) {
+ if (mGidSpecified) {
+ throw new IllegalArgumentException(
+ "Duplicate arg specified");
+ }
+ mGidSpecified = true;
+ mGid = Integer.parseInt(
+ arg.substring(arg.indexOf('=') + 1));
+ } else if (arg.startsWith("--target-sdk-version=")) {
+ if (mTargetSdkVersionSpecified) {
+ throw new IllegalArgumentException(
+ "Duplicate target-sdk-version specified");
+ }
+ mTargetSdkVersionSpecified = true;
+ mTargetSdkVersion = Integer.parseInt(
+ arg.substring(arg.indexOf('=') + 1));
+ } else if (arg.equals("--runtime-args")) {
+ seenRuntimeArgs = true;
+ } else if (arg.startsWith("--runtime-flags=")) {
+ mRuntimeFlags = Integer.parseInt(
+ arg.substring(arg.indexOf('=') + 1));
+ } else if (arg.startsWith("--seinfo=")) {
+ if (mSeInfoSpecified) {
+ throw new IllegalArgumentException(
+ "Duplicate arg specified");
+ }
+ mSeInfoSpecified = true;
+ mSeInfo = arg.substring(arg.indexOf('=') + 1);
+ } else if (arg.startsWith("--capabilities=")) {
+ if (mCapabilitiesSpecified) {
+ throw new IllegalArgumentException(
+ "Duplicate arg specified");
+ }
+ mCapabilitiesSpecified = true;
+ String capString = arg.substring(arg.indexOf('=') + 1);
+
+ String[] capStrings = capString.split(",", 2);
+
+ if (capStrings.length == 1) {
+ mEffectiveCapabilities = Long.decode(capStrings[0]);
+ mPermittedCapabilities = mEffectiveCapabilities;
+ } else {
+ mPermittedCapabilities = Long.decode(capStrings[0]);
+ mEffectiveCapabilities = Long.decode(capStrings[1]);
+ }
+ } else if (arg.startsWith("--rlimit=")) {
+ // Duplicate --rlimit arguments are specifically allowed.
+ String[] limitStrings = arg.substring(arg.indexOf('=') + 1).split(",");
+
+ if (limitStrings.length != 3) {
+ throw new IllegalArgumentException(
+ "--rlimit= should have 3 comma-delimited ints");
+ }
+ int[] rlimitTuple = new int[limitStrings.length];
+
+ for (int i = 0; i < limitStrings.length; i++) {
+ rlimitTuple[i] = Integer.parseInt(limitStrings[i]);
+ }
+
+ if (mRLimits == null) {
+ mRLimits = new ArrayList();
+ }
+
+ mRLimits.add(rlimitTuple);
+ } else if (arg.startsWith("--setgroups=")) {
+ if (mGids != null) {
+ throw new IllegalArgumentException(
+ "Duplicate arg specified");
+ }
+
+ String[] params = arg.substring(arg.indexOf('=') + 1).split(",");
+
+ mGids = new int[params.length];
+
+ for (int i = params.length - 1; i >= 0; i--) {
+ mGids[i] = Integer.parseInt(params[i]);
+ }
+ } else if (arg.equals("--invoke-with")) {
+ if (mInvokeWith != null) {
+ throw new IllegalArgumentException(
+ "Duplicate arg specified");
+ }
+ try {
+ mInvokeWith = args[++curArg];
+ } catch (IndexOutOfBoundsException ex) {
+ throw new IllegalArgumentException(
+ "--invoke-with requires argument");
+ }
+ } else if (arg.startsWith("--nice-name=")) {
+ if (mNiceName != null) {
+ throw new IllegalArgumentException(
+ "Duplicate arg specified");
+ }
+ mNiceName = arg.substring(arg.indexOf('=') + 1);
+ } else if (arg.equals("--mount-external-default")) {
+ mMountExternal = Zygote.MOUNT_EXTERNAL_DEFAULT;
+ } else if (arg.equals("--mount-external-read")) {
+ mMountExternal = Zygote.MOUNT_EXTERNAL_READ;
+ } else if (arg.equals("--mount-external-write")) {
+ mMountExternal = Zygote.MOUNT_EXTERNAL_WRITE;
+ } else if (arg.equals("--mount-external-full")) {
+ mMountExternal = Zygote.MOUNT_EXTERNAL_FULL;
+ } else if (arg.equals("--mount-external-installer")) {
+ mMountExternal = Zygote.MOUNT_EXTERNAL_INSTALLER;
+ } else if (arg.equals("--query-abi-list")) {
+ mAbiListQuery = true;
+ } else if (arg.equals("--get-pid")) {
+ mPidQuery = true;
+ } else if (arg.startsWith("--instruction-set=")) {
+ mInstructionSet = arg.substring(arg.indexOf('=') + 1);
+ } else if (arg.startsWith("--app-data-dir=")) {
+ mAppDataDir = arg.substring(arg.indexOf('=') + 1);
+ } else if (arg.equals("--preload-app")) {
+ mPreloadApp = args[++curArg];
+ } else if (arg.equals("--preload-package")) {
+ mPreloadPackage = args[++curArg];
+ mPreloadPackageLibs = args[++curArg];
+ mPreloadPackageLibFileName = args[++curArg];
+ mPreloadPackageCacheKey = args[++curArg];
+ } else if (arg.equals("--preload-default")) {
+ mPreloadDefault = true;
+ expectRuntimeArgs = false;
+ } else if (arg.equals("--start-child-zygote")) {
+ mStartChildZygote = true;
+ } else if (arg.equals("--set-api-blacklist-exemptions")) {
+ // consume all remaining args; this is a stand-alone command, never included
+ // with the regular fork command.
+ mApiBlacklistExemptions = Arrays.copyOfRange(args, curArg + 1, args.length);
+ curArg = args.length;
+ expectRuntimeArgs = false;
+ } else if (arg.startsWith("--hidden-api-log-sampling-rate=")) {
+ String rateStr = arg.substring(arg.indexOf('=') + 1);
+ try {
+ mHiddenApiAccessLogSampleRate = Integer.parseInt(rateStr);
+ } catch (NumberFormatException nfe) {
+ throw new IllegalArgumentException(
+ "Invalid log sampling rate: " + rateStr, nfe);
+ }
+ expectRuntimeArgs = false;
+ } else if (arg.startsWith("--package-name=")) {
+ if (mPackageName != null) {
+ throw new IllegalArgumentException("Duplicate arg specified");
+ }
+ mPackageName = arg.substring(arg.indexOf('=') + 1);
+ } else if (arg.startsWith("--packages-for-uid=")) {
+ mPackagesForUid = arg.substring(arg.indexOf('=') + 1).split(",");
+ } else if (arg.startsWith("--visible-vols=")) {
+ mVisibleVolIds = arg.substring(arg.indexOf('=') + 1).split(",");
+ } else {
+ break;
+ }
+ }
+
+ if (mAbiListQuery || mPidQuery) {
+ if (args.length - curArg > 0) {
+ throw new IllegalArgumentException("Unexpected arguments after --query-abi-list.");
+ }
+ } else if (mPreloadPackage != null) {
+ if (args.length - curArg > 0) {
+ throw new IllegalArgumentException(
+ "Unexpected arguments after --preload-package.");
+ }
+ } else if (mPreloadApp != null) {
+ if (args.length - curArg > 0) {
+ throw new IllegalArgumentException(
+ "Unexpected arguments after --preload-app.");
+ }
+ } else if (expectRuntimeArgs) {
+ if (!seenRuntimeArgs) {
+ throw new IllegalArgumentException("Unexpected argument : " + args[curArg]);
+ }
+
+ mRemainingArgs = new String[args.length - curArg];
+ System.arraycopy(args, curArg, mRemainingArgs, 0, mRemainingArgs.length);
+ }
+
+ if (mStartChildZygote) {
+ boolean seenChildSocketArg = false;
+ for (String arg : mRemainingArgs) {
+ if (arg.startsWith(Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG)) {
+ seenChildSocketArg = true;
+ break;
+ }
+ }
+ if (!seenChildSocketArg) {
+ throw new IllegalArgumentException("--start-child-zygote specified "
+ + "without " + Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG);
+ }
+ }
+ }
+}
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index ced798c..78ecee1 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -24,16 +24,13 @@
import static android.system.OsConstants.STDOUT_FILENO;
import static com.android.internal.os.ZygoteConnectionConstants.CONNECTION_TIMEOUT_MILLIS;
-import static com.android.internal.os.ZygoteConnectionConstants.MAX_ZYGOTE_ARGC;
import static com.android.internal.os.ZygoteConnectionConstants.WRAPPED_PID_TIMEOUT_MILLIS;
import android.content.pm.ApplicationInfo;
import android.net.Credentials;
import android.net.LocalSocket;
-import android.os.FactoryTest;
import android.os.Parcel;
import android.os.Process;
-import android.os.SystemProperties;
import android.os.Trace;
import android.system.ErrnoException;
import android.system.Os;
@@ -52,8 +49,6 @@
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Base64;
/**
@@ -62,9 +57,6 @@
class ZygoteConnection {
private static final String TAG = "Zygote";
- /** a prototype instance for a future List.toArray() */
- private static final int[][] intArray2d = new int[0][0];
-
/**
* The command socket.
*
@@ -113,7 +105,7 @@
*
* @return null-ok; file descriptor
*/
- FileDescriptor getFileDesciptor() {
+ FileDescriptor getFileDescriptor() {
return mSocket.getFileDescriptor();
}
@@ -127,11 +119,13 @@
*/
Runnable processOneCommand(ZygoteServer zygoteServer) {
String args[];
- Arguments parsedArgs = null;
+ ZygoteArguments parsedArgs = null;
FileDescriptor[] descriptors;
try {
- args = readArgumentList();
+ args = Zygote.readArgumentList(mSocketReader);
+
+ // TODO (chriswailes): Remove this and add an assert.
descriptors = mSocket.getAncillaryFileDescriptors();
} catch (IOException ex) {
throw new IllegalStateException("IOException on command socket", ex);
@@ -148,26 +142,26 @@
FileDescriptor childPipeFd = null;
FileDescriptor serverPipeFd = null;
- parsedArgs = new Arguments(args);
+ parsedArgs = new ZygoteArguments(args);
- if (parsedArgs.abiListQuery) {
+ if (parsedArgs.mAbiListQuery) {
handleAbiListQuery();
return null;
}
- if (parsedArgs.pidQuery) {
+ if (parsedArgs.mPidQuery) {
handlePidQuery();
return null;
}
- if (parsedArgs.preloadDefault) {
+ if (parsedArgs.mPreloadDefault) {
handlePreload();
return null;
}
- if (parsedArgs.preloadPackage != null) {
- handlePreloadPackage(parsedArgs.preloadPackage, parsedArgs.preloadPackageLibs,
- parsedArgs.preloadPackageLibFileName, parsedArgs.preloadPackageCacheKey);
+ if (parsedArgs.mPreloadPackage != null) {
+ handlePreloadPackage(parsedArgs.mPreloadPackage, parsedArgs.mPreloadPackageLibs,
+ parsedArgs.mPreloadPackageLibFileName, parsedArgs.mPreloadPackageCacheKey);
return null;
}
@@ -186,37 +180,37 @@
return null;
}
- if (parsedArgs.apiBlacklistExemptions != null) {
- handleApiBlacklistExemptions(parsedArgs.apiBlacklistExemptions);
+ if (parsedArgs.mApiBlacklistExemptions != null) {
+ handleApiBlacklistExemptions(parsedArgs.mApiBlacklistExemptions);
return null;
}
- if (parsedArgs.hiddenApiAccessLogSampleRate != -1) {
- handleHiddenApiAccessLogSampleRate(parsedArgs.hiddenApiAccessLogSampleRate);
+ if (parsedArgs.mHiddenApiAccessLogSampleRate != -1) {
+ handleHiddenApiAccessLogSampleRate(parsedArgs.mHiddenApiAccessLogSampleRate);
return null;
}
- if (parsedArgs.permittedCapabilities != 0 || parsedArgs.effectiveCapabilities != 0) {
- throw new ZygoteSecurityException("Client may not specify capabilities: " +
- "permitted=0x" + Long.toHexString(parsedArgs.permittedCapabilities) +
- ", effective=0x" + Long.toHexString(parsedArgs.effectiveCapabilities));
+ if (parsedArgs.mPermittedCapabilities != 0 || parsedArgs.mEffectiveCapabilities != 0) {
+ throw new ZygoteSecurityException("Client may not specify capabilities: "
+ + "permitted=0x" + Long.toHexString(parsedArgs.mPermittedCapabilities)
+ + ", effective=0x" + Long.toHexString(parsedArgs.mEffectiveCapabilities));
}
- applyUidSecurityPolicy(parsedArgs, peer);
- applyInvokeWithSecurityPolicy(parsedArgs, peer);
+ Zygote.applyUidSecurityPolicy(parsedArgs, peer);
+ Zygote.applyInvokeWithSecurityPolicy(parsedArgs, peer);
- applyDebuggerSystemProperty(parsedArgs);
- applyInvokeWithSystemProperty(parsedArgs);
+ Zygote.applyDebuggerSystemProperty(parsedArgs);
+ Zygote.applyInvokeWithSystemProperty(parsedArgs);
int[][] rlimits = null;
- if (parsedArgs.rlimits != null) {
- rlimits = parsedArgs.rlimits.toArray(intArray2d);
+ if (parsedArgs.mRLimits != null) {
+ rlimits = parsedArgs.mRLimits.toArray(Zygote.INT_ARRAY_2D);
}
int[] fdsToIgnore = null;
- if (parsedArgs.invokeWith != null) {
+ if (parsedArgs.mInvokeWith != null) {
try {
FileDescriptor[] pipeFds = Os.pipe2(O_CLOEXEC);
childPipeFd = pipeFds[1];
@@ -256,11 +250,11 @@
fd = null;
- pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
- parsedArgs.runtimeFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
- parsedArgs.niceName, fdsToClose, fdsToIgnore, parsedArgs.startChildZygote,
- parsedArgs.instructionSet, parsedArgs.appDataDir, parsedArgs.packageName,
- parsedArgs.packagesForUid, parsedArgs.visibleVolIds);
+ pid = Zygote.forkAndSpecialize(parsedArgs.mUid, parsedArgs.mGid, parsedArgs.mGids,
+ parsedArgs.mRuntimeFlags, rlimits, parsedArgs.mMountExternal, parsedArgs.mSeInfo,
+ parsedArgs.mNiceName, fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote,
+ parsedArgs.mInstructionSet, parsedArgs.mAppDataDir, parsedArgs.mPackageName,
+ parsedArgs.mPackagesForUid, parsedArgs.mVisibleVolIds);
try {
if (pid == 0) {
@@ -272,7 +266,7 @@
serverPipeFd = null;
return handleChildProc(parsedArgs, descriptors, childPipeFd,
- parsedArgs.startChildZygote);
+ parsedArgs.mStartChildZygote);
} else {
// In the parent. A pid < 0 indicates a failure and will be handled in
// handleParentProc.
@@ -387,541 +381,6 @@
}
/**
- * Handles argument parsing for args related to the zygote spawner.
- *
- * Current recognized args:
- * <ul>
- * <li> --setuid=<i>uid of child process, defaults to 0</i>
- * <li> --setgid=<i>gid of child process, defaults to 0</i>
- * <li> --setgroups=<i>comma-separated list of supplimentary gid's</i>
- * <li> --capabilities=<i>a pair of comma-separated integer strings
- * indicating Linux capabilities(2) set for child. The first string
- * represents the <code>permitted</code> set, and the second the
- * <code>effective</code> set. Precede each with 0 or
- * 0x for octal or hexidecimal value. If unspecified, both default to 0.
- * This parameter is only applied if the uid of the new process will
- * be non-0. </i>
- * <li> --rlimit=r,c,m<i>tuple of values for setrlimit() call.
- * <code>r</code> is the resource, <code>c</code> and <code>m</code>
- * are the settings for current and max value.</i>
- * <li> --instruction-set=<i>instruction-set-string</i> which instruction set to use/emulate.
- * <li> --nice-name=<i>nice name to appear in ps</i>
- * <li> --package-name=<i>package name this process belongs to</i>
- * <li> --runtime-args indicates that the remaining arg list should
- * be handed off to com.android.internal.os.RuntimeInit, rather than
- * processed directly.
- * Android runtime startup (eg, Binder initialization) is also eschewed.
- * <li> [--] <args for RuntimeInit >
- * </ul>
- */
- static class Arguments {
- /** from --setuid */
- int uid = 0;
- boolean uidSpecified;
-
- /** from --setgid */
- int gid = 0;
- boolean gidSpecified;
-
- /** from --setgroups */
- int[] gids;
-
- /**
- * From --runtime-flags.
- */
- int runtimeFlags;
-
- /** From --mount-external */
- int mountExternal = Zygote.MOUNT_EXTERNAL_NONE;
-
- /** from --target-sdk-version. */
- int targetSdkVersion;
- boolean targetSdkVersionSpecified;
-
- /** from --nice-name */
- String niceName;
-
- /** from --capabilities */
- boolean capabilitiesSpecified;
- long permittedCapabilities;
- long effectiveCapabilities;
-
- /** from --seinfo */
- boolean seInfoSpecified;
- String seInfo;
-
- /** from all --rlimit=r,c,m */
- ArrayList<int[]> rlimits;
-
- /** from --invoke-with */
- String invokeWith;
-
- /** from --package-name */
- String packageName;
-
- /** from --packages-for-uid */
- String[] packagesForUid;
-
- /** from --visible-vols */
- String[] visibleVolIds;
-
- /**
- * Any args after and including the first non-option arg
- * (or after a '--')
- */
- String remainingArgs[];
-
- /**
- * Whether the current arguments constitute an ABI list query.
- */
- boolean abiListQuery;
-
- /**
- * The instruction set to use, or null when not important.
- */
- String instructionSet;
-
- /**
- * The app data directory. May be null, e.g., for the system server. Note that this might
- * not be reliable in the case of process-sharing apps.
- */
- String appDataDir;
-
- /**
- * The APK path of the package to preload, when using --preload-package.
- */
- String preloadPackage;
-
- /**
- * A Base64 string representing a serialize ApplicationInfo Parcel,
- when using --preload-app.
- */
- String mPreloadApp;
-
- /**
- * The native library path of the package to preload, when using --preload-package.
- */
- String preloadPackageLibs;
-
- /**
- * The filename of the native library to preload, when using --preload-package.
- */
- String preloadPackageLibFileName;
-
- /**
- * The cache key under which to enter the preloaded package into the classloader cache,
- * when using --preload-package.
- */
- String preloadPackageCacheKey;
-
- /**
- * Whether this is a request to start preloading the default resources and classes.
- * This argument only makes sense when the zygote is in lazy preload mode (i.e, when
- * it's started with --enable-lazy-preload).
- */
- boolean preloadDefault;
-
- /**
- * Whether this is a request to start a zygote process as a child of this zygote.
- * Set with --start-child-zygote. The remaining arguments must include the
- * CHILD_ZYGOTE_SOCKET_NAME_ARG flag to indicate the abstract socket name that
- * should be used for communication.
- */
- boolean startChildZygote;
-
- /**
- * Whether the current arguments constitute a request for the zygote's PID.
- */
- boolean pidQuery;
-
- /**
- * Exemptions from API blacklisting. These are sent to the pre-forked zygote at boot time,
- * or when they change, via --set-api-blacklist-exemptions.
- */
- String[] apiBlacklistExemptions;
-
- /**
- * Sampling rate for logging hidden API accesses to the event log. This is sent to the
- * pre-forked zygote at boot time, or when it changes, via --hidden-api-log-sampling-rate.
- */
- int hiddenApiAccessLogSampleRate = -1;
-
- /**
- * Constructs instance and parses args
- * @param args zygote command-line args
- * @throws IllegalArgumentException
- */
- Arguments(String args[]) throws IllegalArgumentException {
- parseArgs(args);
- }
-
- /**
- * Parses the commandline arguments intended for the Zygote spawner
- * (such as "--setuid=" and "--setgid=") and creates an array
- * containing the remaining args.
- *
- * Per security review bug #1112214, duplicate args are disallowed in
- * critical cases to make injection harder.
- */
- private void parseArgs(String args[])
- throws IllegalArgumentException {
- int curArg = 0;
-
- boolean seenRuntimeArgs = false;
-
- boolean expectRuntimeArgs = true;
- for ( /* curArg */ ; curArg < args.length; curArg++) {
- String arg = args[curArg];
-
- if (arg.equals("--")) {
- curArg++;
- break;
- } else if (arg.startsWith("--setuid=")) {
- if (uidSpecified) {
- throw new IllegalArgumentException(
- "Duplicate arg specified");
- }
- uidSpecified = true;
- uid = Integer.parseInt(
- arg.substring(arg.indexOf('=') + 1));
- } else if (arg.startsWith("--setgid=")) {
- if (gidSpecified) {
- throw new IllegalArgumentException(
- "Duplicate arg specified");
- }
- gidSpecified = true;
- gid = Integer.parseInt(
- arg.substring(arg.indexOf('=') + 1));
- } else if (arg.startsWith("--target-sdk-version=")) {
- if (targetSdkVersionSpecified) {
- throw new IllegalArgumentException(
- "Duplicate target-sdk-version specified");
- }
- targetSdkVersionSpecified = true;
- targetSdkVersion = Integer.parseInt(
- arg.substring(arg.indexOf('=') + 1));
- } else if (arg.equals("--runtime-args")) {
- seenRuntimeArgs = true;
- } else if (arg.startsWith("--runtime-flags=")) {
- runtimeFlags = Integer.parseInt(
- arg.substring(arg.indexOf('=') + 1));
- } else if (arg.startsWith("--seinfo=")) {
- if (seInfoSpecified) {
- throw new IllegalArgumentException(
- "Duplicate arg specified");
- }
- seInfoSpecified = true;
- seInfo = arg.substring(arg.indexOf('=') + 1);
- } else if (arg.startsWith("--capabilities=")) {
- if (capabilitiesSpecified) {
- throw new IllegalArgumentException(
- "Duplicate arg specified");
- }
- capabilitiesSpecified = true;
- String capString = arg.substring(arg.indexOf('=')+1);
-
- String[] capStrings = capString.split(",", 2);
-
- if (capStrings.length == 1) {
- effectiveCapabilities = Long.decode(capStrings[0]);
- permittedCapabilities = effectiveCapabilities;
- } else {
- permittedCapabilities = Long.decode(capStrings[0]);
- effectiveCapabilities = Long.decode(capStrings[1]);
- }
- } else if (arg.startsWith("--rlimit=")) {
- // Duplicate --rlimit arguments are specifically allowed.
- String[] limitStrings
- = arg.substring(arg.indexOf('=')+1).split(",");
-
- if (limitStrings.length != 3) {
- throw new IllegalArgumentException(
- "--rlimit= should have 3 comma-delimited ints");
- }
- int[] rlimitTuple = new int[limitStrings.length];
-
- for(int i=0; i < limitStrings.length; i++) {
- rlimitTuple[i] = Integer.parseInt(limitStrings[i]);
- }
-
- if (rlimits == null) {
- rlimits = new ArrayList();
- }
-
- rlimits.add(rlimitTuple);
- } else if (arg.startsWith("--setgroups=")) {
- if (gids != null) {
- throw new IllegalArgumentException(
- "Duplicate arg specified");
- }
-
- String[] params
- = arg.substring(arg.indexOf('=') + 1).split(",");
-
- gids = new int[params.length];
-
- for (int i = params.length - 1; i >= 0 ; i--) {
- gids[i] = Integer.parseInt(params[i]);
- }
- } else if (arg.equals("--invoke-with")) {
- if (invokeWith != null) {
- throw new IllegalArgumentException(
- "Duplicate arg specified");
- }
- try {
- invokeWith = args[++curArg];
- } catch (IndexOutOfBoundsException ex) {
- throw new IllegalArgumentException(
- "--invoke-with requires argument");
- }
- } else if (arg.startsWith("--nice-name=")) {
- if (niceName != null) {
- throw new IllegalArgumentException(
- "Duplicate arg specified");
- }
- niceName = arg.substring(arg.indexOf('=') + 1);
- } else if (arg.equals("--mount-external-default")) {
- mountExternal = Zygote.MOUNT_EXTERNAL_DEFAULT;
- } else if (arg.equals("--mount-external-read")) {
- mountExternal = Zygote.MOUNT_EXTERNAL_READ;
- } else if (arg.equals("--mount-external-write")) {
- mountExternal = Zygote.MOUNT_EXTERNAL_WRITE;
- } else if (arg.equals("--mount-external-full")) {
- mountExternal = Zygote.MOUNT_EXTERNAL_FULL;
- } else if (arg.equals("--mount-external-installer")) {
- mountExternal = Zygote.MOUNT_EXTERNAL_INSTALLER;
- } else if (arg.equals("--mount-external-legacy")) {
- mountExternal = Zygote.MOUNT_EXTERNAL_LEGACY;
- } else if (arg.equals("--query-abi-list")) {
- abiListQuery = true;
- } else if (arg.equals("--get-pid")) {
- pidQuery = true;
- } else if (arg.startsWith("--instruction-set=")) {
- instructionSet = arg.substring(arg.indexOf('=') + 1);
- } else if (arg.startsWith("--app-data-dir=")) {
- appDataDir = arg.substring(arg.indexOf('=') + 1);
- } else if (arg.equals("--preload-app")) {
- mPreloadApp = args[++curArg];
- } else if (arg.equals("--preload-package")) {
- preloadPackage = args[++curArg];
- preloadPackageLibs = args[++curArg];
- preloadPackageLibFileName = args[++curArg];
- preloadPackageCacheKey = args[++curArg];
- } else if (arg.equals("--preload-default")) {
- preloadDefault = true;
- expectRuntimeArgs = false;
- } else if (arg.equals("--start-child-zygote")) {
- startChildZygote = true;
- } else if (arg.equals("--set-api-blacklist-exemptions")) {
- // consume all remaining args; this is a stand-alone command, never included
- // with the regular fork command.
- apiBlacklistExemptions = Arrays.copyOfRange(args, curArg + 1, args.length);
- curArg = args.length;
- expectRuntimeArgs = false;
- } else if (arg.startsWith("--hidden-api-log-sampling-rate=")) {
- String rateStr = arg.substring(arg.indexOf('=') + 1);
- try {
- hiddenApiAccessLogSampleRate = Integer.parseInt(rateStr);
- } catch (NumberFormatException nfe) {
- throw new IllegalArgumentException(
- "Invalid log sampling rate: " + rateStr, nfe);
- }
- expectRuntimeArgs = false;
- } else if (arg.startsWith("--package-name=")) {
- if (packageName != null) {
- throw new IllegalArgumentException("Duplicate arg specified");
- }
- packageName = arg.substring(arg.indexOf('=') + 1);
- } else if (arg.startsWith("--packages-for-uid=")) {
- packagesForUid = arg.substring(arg.indexOf('=') + 1).split(",");
- } else if (arg.startsWith("--visible-vols=")) {
- visibleVolIds = arg.substring(arg.indexOf('=') + 1).split(",");
- } else {
- break;
- }
- }
-
- if (abiListQuery || pidQuery) {
- if (args.length - curArg > 0) {
- throw new IllegalArgumentException("Unexpected arguments after --query-abi-list.");
- }
- } else if (preloadPackage != null) {
- if (args.length - curArg > 0) {
- throw new IllegalArgumentException(
- "Unexpected arguments after --preload-package.");
- }
- } else if (mPreloadApp != null) {
- if (args.length - curArg > 0) {
- throw new IllegalArgumentException(
- "Unexpected arguments after --preload-app.");
- }
- } else if (expectRuntimeArgs) {
- if (!seenRuntimeArgs) {
- throw new IllegalArgumentException("Unexpected argument : " + args[curArg]);
- }
-
- remainingArgs = new String[args.length - curArg];
- System.arraycopy(args, curArg, remainingArgs, 0, remainingArgs.length);
- }
-
- if (startChildZygote) {
- boolean seenChildSocketArg = false;
- for (String arg : remainingArgs) {
- if (arg.startsWith(Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG)) {
- seenChildSocketArg = true;
- break;
- }
- }
- if (!seenChildSocketArg) {
- throw new IllegalArgumentException("--start-child-zygote specified " +
- "without " + Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG);
- }
- }
- }
- }
-
- /**
- * Reads an argument list from the command socket/
- * @return Argument list or null if EOF is reached
- * @throws IOException passed straight through
- */
- private String[] readArgumentList()
- throws IOException {
-
- /**
- * See android.os.Process.zygoteSendArgsAndGetPid()
- * Presently the wire format to the zygote process is:
- * a) a count of arguments (argc, in essence)
- * b) a number of newline-separated argument strings equal to count
- *
- * After the zygote process reads these it will write the pid of
- * the child or -1 on failure.
- */
-
- int argc;
-
- try {
- String s = mSocketReader.readLine();
-
- if (s == null) {
- // EOF reached.
- return null;
- }
- argc = Integer.parseInt(s);
- } catch (NumberFormatException ex) {
- Log.e(TAG, "invalid Zygote wire format: non-int at argc");
- throw new IOException("invalid wire format");
- }
-
- // See bug 1092107: large argc can be used for a DOS attack
- if (argc > MAX_ZYGOTE_ARGC) {
- throw new IOException("max arg count exceeded");
- }
-
- String[] result = new String[argc];
- for (int i = 0; i < argc; i++) {
- result[i] = mSocketReader.readLine();
- if (result[i] == null) {
- // We got an unexpected EOF.
- throw new IOException("truncated request");
- }
- }
-
- return result;
- }
-
- /**
- * uid 1000 (Process.SYSTEM_UID) may specify any uid > 1000 in normal
- * operation. It may also specify any gid and setgroups() list it chooses.
- * In factory test mode, it may specify any UID.
- *
- * @param args non-null; zygote spawner arguments
- * @param peer non-null; peer credentials
- * @throws ZygoteSecurityException
- */
- private static void applyUidSecurityPolicy(Arguments args, Credentials peer)
- throws ZygoteSecurityException {
-
- if (peer.getUid() == Process.SYSTEM_UID) {
- /* In normal operation, SYSTEM_UID can only specify a restricted
- * set of UIDs. In factory test mode, SYSTEM_UID may specify any uid.
- */
- boolean uidRestricted = FactoryTest.getMode() == FactoryTest.FACTORY_TEST_OFF;
-
- if (uidRestricted && args.uidSpecified && (args.uid < Process.SYSTEM_UID)) {
- throw new ZygoteSecurityException(
- "System UID may not launch process with UID < "
- + Process.SYSTEM_UID);
- }
- }
-
- // If not otherwise specified, uid and gid are inherited from peer
- if (!args.uidSpecified) {
- args.uid = peer.getUid();
- args.uidSpecified = true;
- }
- if (!args.gidSpecified) {
- args.gid = peer.getGid();
- args.gidSpecified = true;
- }
- }
-
- /**
- * Applies debugger system properties to the zygote arguments.
- *
- * If "ro.debuggable" is "1", all apps are debuggable. Otherwise,
- * the debugger state is specified via the "--enable-jdwp" flag
- * in the spawn request.
- *
- * @param args non-null; zygote spawner args
- */
- public static void applyDebuggerSystemProperty(Arguments args) {
- if (RoSystemProperties.DEBUGGABLE) {
- args.runtimeFlags |= Zygote.DEBUG_ENABLE_JDWP;
- }
- }
-
- /**
- * Applies zygote security policy.
- * Based on the credentials of the process issuing a zygote command:
- * <ol>
- * <li> uid 0 (root) may specify --invoke-with to launch Zygote with a
- * wrapper command.
- * <li> Any other uid may not specify any invoke-with argument.
- * </ul>
- *
- * @param args non-null; zygote spawner arguments
- * @param peer non-null; peer credentials
- * @throws ZygoteSecurityException
- */
- private static void applyInvokeWithSecurityPolicy(Arguments args, Credentials peer)
- throws ZygoteSecurityException {
- int peerUid = peer.getUid();
-
- if (args.invokeWith != null && peerUid != 0 &&
- (args.runtimeFlags & Zygote.DEBUG_ENABLE_JDWP) == 0) {
- throw new ZygoteSecurityException("Peer is permitted to specify an"
- + "explicit invoke-with wrapper command only for debuggable"
- + "applications.");
- }
- }
-
- /**
- * Applies invoke-with system properties to the zygote arguments.
- *
- * @param args non-null; zygote args
- */
- public static void applyInvokeWithSystemProperty(Arguments args) {
- if (args.invokeWith == null && args.niceName != null) {
- String property = "wrap." + args.niceName;
- args.invokeWith = SystemProperties.get(property);
- if (args.invokeWith != null && args.invokeWith.length() == 0) {
- args.invokeWith = null;
- }
- }
- }
-
- /**
* Handles post-fork setup of child proc, closing sockets as appropriate,
* reopen stdio as appropriate, and ultimately throwing MethodAndArgsCaller
* if successful or returning if failed.
@@ -931,7 +390,7 @@
* @param pipeFd null-ok; pipe for communication back to Zygote.
* @param isZygote whether this new child process is itself a new Zygote.
*/
- private Runnable handleChildProc(Arguments parsedArgs, FileDescriptor[] descriptors,
+ private Runnable handleChildProc(ZygoteArguments parsedArgs, FileDescriptor[] descriptors,
FileDescriptor pipeFd, boolean isZygote) {
/**
* By the time we get here, the native code has closed the two actual Zygote
@@ -954,27 +413,27 @@
}
}
- if (parsedArgs.niceName != null) {
- Process.setArgV0(parsedArgs.niceName);
+ if (parsedArgs.mNiceName != null) {
+ Process.setArgV0(parsedArgs.mNiceName);
}
// End of the postFork event.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- if (parsedArgs.invokeWith != null) {
- WrapperInit.execApplication(parsedArgs.invokeWith,
- parsedArgs.niceName, parsedArgs.targetSdkVersion,
+ if (parsedArgs.mInvokeWith != null) {
+ WrapperInit.execApplication(parsedArgs.mInvokeWith,
+ parsedArgs.mNiceName, parsedArgs.mTargetSdkVersion,
VMRuntime.getCurrentInstructionSet(),
- pipeFd, parsedArgs.remainingArgs);
+ pipeFd, parsedArgs.mRemainingArgs);
// Should not get here.
throw new IllegalStateException("WrapperInit.execApplication unexpectedly returned");
} else {
if (!isZygote) {
- return ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs,
- null /* classLoader */);
+ return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,
+ parsedArgs.mRemainingArgs, null /* classLoader */);
} else {
- return ZygoteInit.childZygoteInit(parsedArgs.targetSdkVersion,
- parsedArgs.remainingArgs, null /* classLoader */);
+ return ZygoteInit.childZygoteInit(parsedArgs.mTargetSdkVersion,
+ parsedArgs.mRemainingArgs, null /* classLoader */);
}
}
}
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index c2c6ae6..2f00c07 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -21,7 +21,6 @@
import android.content.res.Resources;
import android.content.res.TypedArray;
-import android.opengl.EGL14;
import android.os.Build;
import android.os.Environment;
import android.os.IInstalld;
@@ -71,16 +70,16 @@
/**
* Startup class for the zygote process.
*
- * Pre-initializes some classes, and then waits for commands on a UNIX domain
- * socket. Based on these commands, forks off child processes that inherit
- * the initial state of the VM.
+ * Pre-initializes some classes, and then waits for commands on a UNIX domain socket. Based on these
+ * commands, forks off child processes that inherit the initial state of the VM.
*
- * Please see {@link ZygoteConnection.Arguments} for documentation on the
- * client protocol.
+ * Please see {@link ZygoteArguments} for documentation on the client protocol.
*
* @hide
*/
public class ZygoteInit {
+
+ // TODO (chriswailes): Change this so it is set with Zygote or ZygoteSecondary as appropriate
private static final String TAG = "Zygote";
private static final String PROPERTY_DISABLE_OPENGL_PRELOADING = "ro.zygote.disable_gl_preload";
@@ -89,11 +88,15 @@
private static final int LOG_BOOT_PROGRESS_PRELOAD_START = 3020;
private static final int LOG_BOOT_PROGRESS_PRELOAD_END = 3030;
- /** when preloading, GC after allocating this many bytes */
+ /**
+ * when preloading, GC after allocating this many bytes
+ */
private static final int PRELOAD_GC_THRESHOLD = 50000;
private static final String ABI_LIST_ARG = "--abi-list=";
+ // TODO (chriswailes): Re-name this --zygote-socket-name= and then add a
+ // --blastula-socket-name parameter.
private static final String SOCKET_NAME_ARG = "--socket-name=";
/**
@@ -106,7 +109,9 @@
*/
private static final String PRELOADED_CLASSES = "/system/etc/preloaded-classes";
- /** Controls whether we should preload resources during zygote init. */
+ /**
+ * Controls whether we should preload resources during zygote init.
+ */
public static final boolean PRELOAD_RESOURCES = true;
private static final int UNPRIVILEGED_UID = 9999;
@@ -173,6 +178,7 @@
}
native private static void nativePreloadAppProcessHALs();
+
native private static void nativePreloadOpenGL();
private static void preloadOpenGL() {
@@ -191,8 +197,8 @@
/**
* Register AndroidKeyStoreProvider and warm up the providers that are already registered.
*
- * By doing it here we avoid that each app does it when requesting a service from the
- * provider for the first time.
+ * By doing it here we avoid that each app does it when requesting a service from the provider
+ * for the first time.
*/
private static void warmUpJcaProviders() {
long startTime = SystemClock.uptimeMillis();
@@ -218,11 +224,10 @@
}
/**
- * Performs Zygote process initialization. Loads and initializes
- * commonly used classes.
+ * Performs Zygote process initialization. Loads and initializes commonly used classes.
*
- * Most classes only cause a few hundred bytes to be allocated, but
- * a few will allocate a dozen Kbytes (in one case, 500+K).
+ * Most classes only cause a few hundred bytes to be allocated, but a few will allocate a dozen
+ * Kbytes (in one case, 500+K).
*/
private static void preloadClasses() {
final VMRuntime runtime = VMRuntime.getRuntime();
@@ -263,8 +268,8 @@
runtime.setTargetHeapUtilization(0.8f);
try {
- BufferedReader br
- = new BufferedReader(new InputStreamReader(is), 256);
+ BufferedReader br =
+ new BufferedReader(new InputStreamReader(is), 256);
int count = 0;
String line;
@@ -305,7 +310,7 @@
}
Log.i(TAG, "...preloaded " + count + " classes in "
- + (SystemClock.uptimeMillis()-startTime) + "ms.");
+ + (SystemClock.uptimeMillis() - startTime) + "ms.");
} catch (IOException e) {
Log.e(TAG, "Error reading " + PRELOADED_CLASSES + ".", e);
} finally {
@@ -331,11 +336,10 @@
}
/**
- * Load in commonly used resources, so they can be shared across
- * processes.
+ * Load in commonly used resources, so they can be shared across processes.
*
- * These tend to be a few Kbytes, but are frequently in the 20-40K
- * range, and occasionally even larger.
+ * These tend to be a few Kbytes, but are frequently in the 20-40K range, and occasionally even
+ * larger.
*/
private static void preloadResources() {
final VMRuntime runtime = VMRuntime.getRuntime();
@@ -352,7 +356,7 @@
int N = preloadDrawables(ar);
ar.recycle();
Log.i(TAG, "...preloaded " + N + " resources in "
- + (SystemClock.uptimeMillis()-startTime) + "ms.");
+ + (SystemClock.uptimeMillis() - startTime) + "ms.");
startTime = SystemClock.uptimeMillis();
ar = mResources.obtainTypedArray(
@@ -360,7 +364,7 @@
N = preloadColorStateLists(ar);
ar.recycle();
Log.i(TAG, "...preloaded " + N + " resources in "
- + (SystemClock.uptimeMillis()-startTime) + "ms.");
+ + (SystemClock.uptimeMillis() - startTime) + "ms.");
if (mResources.getBoolean(
com.android.internal.R.bool.config_freeformWindowManagement)) {
@@ -381,7 +385,7 @@
private static int preloadColorStateLists(TypedArray ar) {
int N = ar.length();
- for (int i=0; i<N; i++) {
+ for (int i = 0; i < N; i++) {
int id = ar.getResourceId(i, 0);
if (false) {
Log.v(TAG, "Preloading resource #" + Integer.toHexString(id));
@@ -390,8 +394,8 @@
if (mResources.getColorStateList(id, null) == null) {
throw new IllegalArgumentException(
"Unable to find preloaded color resource #0x"
- + Integer.toHexString(id)
- + " (" + ar.getString(i) + ")");
+ + Integer.toHexString(id)
+ + " (" + ar.getString(i) + ")");
}
}
}
@@ -401,7 +405,7 @@
private static int preloadDrawables(TypedArray ar) {
int N = ar.length();
- for (int i=0; i<N; i++) {
+ for (int i = 0; i < N; i++) {
int id = ar.getResourceId(i, 0);
if (false) {
Log.v(TAG, "Preloading resource #" + Integer.toHexString(id));
@@ -410,8 +414,8 @@
if (mResources.getDrawable(id, null) == null) {
throw new IllegalArgumentException(
"Unable to find preloaded drawable resource #0x"
- + Integer.toHexString(id)
- + " (" + ar.getString(i) + ")");
+ + Integer.toHexString(id)
+ + " (" + ar.getString(i) + ")");
}
}
}
@@ -419,9 +423,8 @@
}
/**
- * Runs several special GCs to try to clean up a few generations of
- * softly- and final-reachable objects, along with any other garbage.
- * This is only useful just before a fork().
+ * Runs several special GCs to try to clean up a few generations of softly- and final-reachable
+ * objects, along with any other garbage. This is only useful just before a fork().
*/
private static void gcAndFinalize() {
ZygoteHooks.gcAndFinalize();
@@ -430,12 +433,12 @@
/**
* Finish remaining work for the newly forked system server process.
*/
- private static Runnable handleSystemServerProcess(ZygoteConnection.Arguments parsedArgs) {
+ private static Runnable handleSystemServerProcess(ZygoteArguments parsedArgs) {
// set umask to 0077 so new files and directories will default to owner-only permissions.
Os.umask(S_IRWXG | S_IRWXO);
- if (parsedArgs.niceName != null) {
- Process.setArgV0(parsedArgs.niceName);
+ if (parsedArgs.mNiceName != null) {
+ Process.setArgV0(parsedArgs.mNiceName);
}
final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH");
@@ -454,8 +457,8 @@
}
}
- if (parsedArgs.invokeWith != null) {
- String[] args = parsedArgs.remainingArgs;
+ if (parsedArgs.mInvokeWith != null) {
+ String[] args = parsedArgs.mRemainingArgs;
// If we have a non-null system server class path, we'll have to duplicate the
// existing arguments and append the classpath to it. ART will handle the classpath
// correctly when we exec a new process.
@@ -467,15 +470,15 @@
args = amendedArgs;
}
- WrapperInit.execApplication(parsedArgs.invokeWith,
- parsedArgs.niceName, parsedArgs.targetSdkVersion,
+ WrapperInit.execApplication(parsedArgs.mInvokeWith,
+ parsedArgs.mNiceName, parsedArgs.mTargetSdkVersion,
VMRuntime.getCurrentInstructionSet(), null, args);
throw new IllegalStateException("Unexpected return from WrapperInit.execApplication");
} else {
ClassLoader cl = null;
if (systemServerClasspath != null) {
- cl = createPathClassLoader(systemServerClasspath, parsedArgs.targetSdkVersion);
+ cl = createPathClassLoader(systemServerClasspath, parsedArgs.mTargetSdkVersion);
Thread.currentThread().setContextClassLoader(cl);
}
@@ -483,16 +486,17 @@
/*
* Pass the remaining arguments to SystemServer.
*/
- return ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl);
+ return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,
+ parsedArgs.mRemainingArgs, cl);
}
/* should never reach here */
}
/**
- * Note that preparing the profiles for system server does not require special
- * selinux permissions. From the installer perspective the system server is a regular package
- * which can capture profile information.
+ * Note that preparing the profiles for system server does not require special selinux
+ * permissions. From the installer perspective the system server is a regular package which can
+ * capture profile information.
*/
private static void prepareSystemServerProfile(String systemServerClasspath)
throws RemoteException {
@@ -544,8 +548,8 @@
}
/**
- * Performs dex-opt on the elements of {@code classPath}, if needed. We
- * choose the instruction set of the current runtime.
+ * Performs dex-opt on the elements of {@code classPath}, if needed. We choose the instruction
+ * set of the current runtime.
*/
private static void performSystemServerDexOpt(String classPath) {
final String[] classPathElements = classPath.split(":");
@@ -563,8 +567,9 @@
int dexoptNeeded;
try {
dexoptNeeded = DexFile.getDexOptNeeded(
- classPathElement, instructionSet, systemServerFilter,
- null /* classLoaderContext */, false /* newProfile */, false /* downgrade */);
+ classPathElement, instructionSet, systemServerFilter,
+ null /* classLoaderContext */, false /* newProfile */,
+ false /* downgrade */);
} catch (FileNotFoundException ignored) {
// Do not add to the classpath.
Log.w(TAG, "Missing classpath element for system server: " + classPathElement);
@@ -607,8 +612,8 @@
}
/**
- * Encodes the system server class loader context in a format that is accepted by dexopt.
- * This assumes the system server is always loaded with a {@link dalvik.system.PathClassLoader}.
+ * Encodes the system server class loader context in a format that is accepted by dexopt. This
+ * assumes the system server is always loaded with a {@link dalvik.system.PathClassLoader}.
*
* Note that ideally we would use the {@code DexoptUtils} to compute this. However we have no
* dependency here on the server so we hard code the logic again.
@@ -619,10 +624,11 @@
/**
* Encodes the class path in a format accepted by dexopt.
- * @param classPath the old class path (may be empty).
- * @param newElement the new class path elements
- * @return the class path encoding resulted from appending {@code newElement} to
- * {@code classPath}.
+ *
+ * @param classPath The old class path (may be empty).
+ * @param newElement The new class path elements
+ * @return The class path encoding resulted from appending {@code newElement} to {@code
+ * classPath}.
*/
private static String encodeSystemServerClassPath(String classPath, String newElement) {
return (classPath == null || classPath.isEmpty())
@@ -633,25 +639,25 @@
/**
* Prepare the arguments and forks for the system server process.
*
- * Returns an {@code Runnable} that provides an entrypoint into system_server code in the
- * child process, and {@code null} in the parent.
+ * @return A {@code Runnable} that provides an entrypoint into system_server code in the child
+ * process; {@code null} in the parent.
*/
private static Runnable forkSystemServer(String abiList, String socketName,
ZygoteServer zygoteServer) {
long capabilities = posixCapabilitiesAsBits(
- OsConstants.CAP_IPC_LOCK,
- OsConstants.CAP_KILL,
- OsConstants.CAP_NET_ADMIN,
- OsConstants.CAP_NET_BIND_SERVICE,
- OsConstants.CAP_NET_BROADCAST,
- OsConstants.CAP_NET_RAW,
- OsConstants.CAP_SYS_MODULE,
- OsConstants.CAP_SYS_NICE,
- OsConstants.CAP_SYS_PTRACE,
- OsConstants.CAP_SYS_TIME,
- OsConstants.CAP_SYS_TTY_CONFIG,
- OsConstants.CAP_WAKE_ALARM,
- OsConstants.CAP_BLOCK_SUSPEND
+ OsConstants.CAP_IPC_LOCK,
+ OsConstants.CAP_KILL,
+ OsConstants.CAP_NET_ADMIN,
+ OsConstants.CAP_NET_BIND_SERVICE,
+ OsConstants.CAP_NET_BROADCAST,
+ OsConstants.CAP_NET_RAW,
+ OsConstants.CAP_SYS_MODULE,
+ OsConstants.CAP_SYS_NICE,
+ OsConstants.CAP_SYS_PTRACE,
+ OsConstants.CAP_SYS_TIME,
+ OsConstants.CAP_SYS_TTY_CONFIG,
+ OsConstants.CAP_WAKE_ALARM,
+ OsConstants.CAP_BLOCK_SUSPEND
);
/* Containers run without some capabilities, so drop any caps that are not available. */
StructCapUserHeader header = new StructCapUserHeader(
@@ -666,38 +672,39 @@
/* Hardcoded command line to start the system server */
String args[] = {
- "--setuid=1000",
- "--setgid=1000",
- "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023,1024,1032,1065,3001,3002,3003,3006,3007,3009,3010",
- "--capabilities=" + capabilities + "," + capabilities,
- "--nice-name=system_server",
- "--runtime-args",
- "--target-sdk-version=" + VMRuntime.SDK_VERSION_CUR_DEVELOPMENT,
- "com.android.server.SystemServer",
+ "--setuid=1000",
+ "--setgid=1000",
+ "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023,"
+ + "1024,1032,1065,3001,3002,3003,3006,3007,3009,3010",
+ "--capabilities=" + capabilities + "," + capabilities,
+ "--nice-name=system_server",
+ "--runtime-args",
+ "--target-sdk-version=" + VMRuntime.SDK_VERSION_CUR_DEVELOPMENT,
+ "com.android.server.SystemServer",
};
- ZygoteConnection.Arguments parsedArgs = null;
+ ZygoteArguments parsedArgs = null;
int pid;
try {
- parsedArgs = new ZygoteConnection.Arguments(args);
- ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
- ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);
+ parsedArgs = new ZygoteArguments(args);
+ Zygote.applyDebuggerSystemProperty(parsedArgs);
+ Zygote.applyInvokeWithSystemProperty(parsedArgs);
boolean profileSystemServer = SystemProperties.getBoolean(
"dalvik.vm.profilesystemserver", false);
if (profileSystemServer) {
- parsedArgs.runtimeFlags |= Zygote.PROFILE_SYSTEM_SERVER;
+ parsedArgs.mRuntimeFlags |= Zygote.PROFILE_SYSTEM_SERVER;
}
/* Request to fork the system server process */
pid = Zygote.forkSystemServer(
- parsedArgs.uid, parsedArgs.gid,
- parsedArgs.gids,
- parsedArgs.runtimeFlags,
+ parsedArgs.mUid, parsedArgs.mGid,
+ parsedArgs.mGids,
+ parsedArgs.mRuntimeFlags,
null,
- parsedArgs.permittedCapabilities,
- parsedArgs.effectiveCapabilities);
+ parsedArgs.mPermittedCapabilities,
+ parsedArgs.mEffectiveCapabilities);
} catch (IllegalArgumentException ex) {
throw new RuntimeException(ex);
}
@@ -785,10 +792,10 @@
if (!enableLazyPreload) {
bootTimingsTraceLog.traceBegin("ZygotePreload");
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
- SystemClock.uptimeMillis());
+ SystemClock.uptimeMillis());
preload(bootTimingsTraceLog);
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
- SystemClock.uptimeMillis());
+ SystemClock.uptimeMillis());
bootTimingsTraceLog.traceEnd(); // ZygotePreload
} else {
Zygote.resetNicePriority();
@@ -844,17 +851,16 @@
/**
* Return {@code true} if this device configuration has another zygote.
*
- * We determine this by comparing the device ABI list with this zygotes
- * list. If this zygote supports all ABIs this device supports, there won't
- * be another zygote.
+ * We determine this by comparing the device ABI list with this zygotes list. If this zygote
+ * supports all ABIs this device supports, there won't be another zygote.
*/
private static boolean hasSecondZygote(String abiList) {
return !SystemProperties.get("ro.product.cpu.abilist").equals(abiList);
}
private static void waitForSecondaryZygote(String socketName) {
- String otherZygoteName = Process.ZYGOTE_SOCKET.equals(socketName) ?
- Process.SECONDARY_ZYGOTE_SOCKET : Process.ZYGOTE_SOCKET;
+ String otherZygoteName = ZygoteProcess.ZYGOTE_SOCKET_NAME.equals(socketName)
+ ? ZygoteProcess.ZYGOTE_SECONDARY_SOCKET_NAME : ZygoteProcess.ZYGOTE_SOCKET_NAME;
ZygoteProcess.waitForConnectionToZygote(otherZygoteName);
}
@@ -869,9 +875,8 @@
}
/**
- * The main function called when started through the zygote process. This
- * could be unified with main(), if the native code in nativeFinishInit()
- * were rationalized with Zygote startup.<p>
+ * The main function called when started through the zygote process. This could be unified with
+ * main(), if the native code in nativeFinishInit() were rationalized with Zygote startup.<p>
*
* Current recognized args:
* <ul>
@@ -881,7 +886,8 @@
* @param targetSdkVersion target SDK version
* @param argv arg strings
*/
- public static final Runnable zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader) {
+ public static final Runnable zygoteInit(int targetSdkVersion, String[] argv,
+ ClassLoader classLoader) {
if (RuntimeInit.DEBUG) {
Slog.d(RuntimeInit.TAG, "RuntimeInit: Starting application from zygote");
}
@@ -895,9 +901,9 @@
}
/**
- * The main function called when starting a child zygote process. This is used as an
- * alternative to zygoteInit(), which skips calling into initialization routines that
- * start the Binder threadpool.
+ * The main function called when starting a child zygote process. This is used as an alternative
+ * to zygoteInit(), which skips calling into initialization routines that start the Binder
+ * threadpool.
*/
static final Runnable childZygoteInit(
int targetSdkVersion, String[] argv, ClassLoader classLoader) {
diff --git a/core/java/com/android/internal/os/ZygoteServer.java b/core/java/com/android/internal/os/ZygoteServer.java
index fecf9b9..c1bfde1 100644
--- a/core/java/com/android/internal/os/ZygoteServer.java
+++ b/core/java/com/android/internal/os/ZygoteServer.java
@@ -20,14 +20,14 @@
import android.net.LocalServerSocket;
import android.net.LocalSocket;
-import android.system.Os;
import android.system.ErrnoException;
+import android.system.Os;
import android.system.StructPollfd;
import android.util.Log;
-
import android.util.Slog;
-import java.io.IOException;
+
import java.io.FileDescriptor;
+import java.io.IOException;
import java.util.ArrayList;
/**
@@ -63,8 +63,7 @@
*/
private boolean mIsForkChild;
- ZygoteServer() {
- }
+ ZygoteServer() { }
void setForkChild() {
mIsForkChild = true;
@@ -197,7 +196,7 @@
if (i == 0) {
ZygoteConnection newPeer = acceptCommandPeer(abiList);
peers.add(newPeer);
- fds.add(newPeer.getFileDesciptor());
+ fds.add(newPeer.getFileDescriptor());
} else {
try {
ZygoteConnection connection = peers.get(i);
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 8194a92..0752efe 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -64,7 +64,6 @@
void showInputMethodAndSubtypeEnablerFromClient(in IInputMethodClient client, String topId);
boolean isInputMethodPickerShownForTest();
InputMethodSubtype getCurrentInputMethodSubtype();
- boolean setCurrentInputMethodSubtype(in InputMethodSubtype subtype);
void setAdditionalInputMethodSubtypes(String id, in InputMethodSubtype[] subtypes);
// This is kept due to @UnsupportedAppUsage.
// TODO(Bug 113914148): Consider removing this.
diff --git a/core/jni/android/graphics/Picture.cpp b/core/jni/android/graphics/Picture.cpp
index fd1d87f..d29857d 100644
--- a/core/jni/android/graphics/Picture.cpp
+++ b/core/jni/android/graphics/Picture.cpp
@@ -37,6 +37,12 @@
}
}
+Picture::Picture(sk_sp<SkPicture>&& src) {
+ mPicture = std::move(src);
+ mWidth = 0;
+ mHeight = 0;
+}
+
Canvas* Picture::beginRecording(int width, int height) {
mPicture.reset(NULL);
mRecorder.reset(new SkPictureRecorder);
diff --git a/core/jni/android/graphics/Picture.h b/core/jni/android/graphics/Picture.h
index 3068631..536f651 100644
--- a/core/jni/android/graphics/Picture.h
+++ b/core/jni/android/graphics/Picture.h
@@ -37,6 +37,7 @@
class Picture {
public:
explicit Picture(const Picture* src = NULL);
+ explicit Picture(sk_sp<SkPicture>&& src);
Canvas* beginRecording(int width, int height);
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 5a8ab3c..318ec9b 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -48,6 +48,7 @@
#include <FrameInfo.h>
#include <FrameMetricsObserver.h>
#include <IContextFactory.h>
+#include <Picture.h>
#include <Properties.h>
#include <PropertyValuesAnimatorSet.h>
#include <RenderNode.h>
@@ -71,6 +72,11 @@
} gFrameMetricsObserverClassInfo;
struct {
+ jclass clazz;
+ jmethodID invokePictureCapturedCallback;
+} gHardwareRenderer;
+
+struct {
jmethodID onFrameDraw;
} gFrameDrawingCallback;
@@ -905,6 +911,27 @@
jobject mObject;
};
+static void android_view_ThreadedRenderer_setPictureCapturedCallbackJNI(JNIEnv* env,
+ jobject clazz, jlong proxyPtr, jobject pictureCallback) {
+ RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+ if (!pictureCallback) {
+ proxy->setPictureCapturedCallback(nullptr);
+ } else {
+ JavaVM* vm = nullptr;
+ LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&vm) != JNI_OK, "Unable to get Java VM");
+ auto globalCallbackRef = std::make_shared<JGlobalRefHolder>(vm,
+ env->NewGlobalRef(pictureCallback));
+ proxy->setPictureCapturedCallback([globalCallbackRef](sk_sp<SkPicture>&& picture) {
+ JNIEnv* env = getenv(globalCallbackRef->vm());
+ Picture* wrapper = new Picture{std::move(picture)};
+ env->CallStaticVoidMethod(gHardwareRenderer.clazz,
+ gHardwareRenderer.invokePictureCapturedCallback,
+ static_cast<jlong>(reinterpret_cast<intptr_t>(wrapper)),
+ globalCallbackRef->object());
+ });
+ }
+}
+
static void android_view_ThreadedRenderer_setFrameCallback(JNIEnv* env,
jobject clazz, jlong proxyPtr, jobject frameCallback) {
RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
@@ -1145,6 +1172,8 @@
{ "nRemoveRenderNode", "(JJ)V", (void*) android_view_ThreadedRenderer_removeRenderNode},
{ "nDrawRenderNode", "(JJ)V", (void*) android_view_ThreadedRendererd_drawRenderNode},
{ "nSetContentDrawBounds", "(JIIII)V", (void*)android_view_ThreadedRenderer_setContentDrawBounds},
+ { "nSetPictureCaptureCallback", "(JLandroid/graphics/HardwareRenderer$PictureCapturedCallback;)V",
+ (void*) android_view_ThreadedRenderer_setPictureCapturedCallbackJNI },
{ "nSetFrameCallback", "(JLandroid/graphics/HardwareRenderer$FrameDrawingCallback;)V",
(void*)android_view_ThreadedRenderer_setFrameCallback},
{ "nSetFrameCompleteCallback", "(JLandroid/graphics/HardwareRenderer$FrameCompleteCallback;)V",
@@ -1198,6 +1227,13 @@
gFrameMetricsObserverClassInfo.timingDataBuffer = GetFieldIDOrDie(
env, metricsClass, "mTimingData", "[J");
+ jclass hardwareRenderer = FindClassOrDie(env,
+ "android/graphics/HardwareRenderer");
+ gHardwareRenderer.clazz = reinterpret_cast<jclass>(env->NewGlobalRef(hardwareRenderer));
+ gHardwareRenderer.invokePictureCapturedCallback = GetStaticMethodIDOrDie(env, hardwareRenderer,
+ "invokePictureCapturedCallback",
+ "(JLandroid/graphics/HardwareRenderer$PictureCapturedCallback;)V");
+
jclass frameCallbackClass = FindClassOrDie(env,
"android/graphics/HardwareRenderer$FrameDrawingCallback");
gFrameDrawingCallback.onFrameDraw = GetMethodIDOrDie(env, frameCallbackClass,
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index f68c760..6cdba33 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -2179,4 +2179,8 @@
// OPEN: Settings > Network & internet > Click Mobile network to land on a page with a list of
// SIM/eSIM subscriptions.
MOBILE_NETWORK_LIST = 1627;
+
+ // OPEN: Settings > Display > Adaptive sleep
+ // OS: Q
+ SETTINGS_ADAPTIVE_SLEEP = 1628;
}
diff --git a/core/proto/android/debug/enums.proto b/core/proto/android/debug/enums.proto
new file mode 100644
index 0000000..6747bb7
--- /dev/null
+++ b/core/proto/android/debug/enums.proto
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+package android.debug;
+
+option java_outer_classname = "AdbProtoEnums";
+option java_multiple_files = true;
+
+/**
+ * adb connection state used to track adb connection changes in AdbDebuggingManager.java.
+ */
+enum AdbConnectionStateEnum {
+ UNKNOWN = 0;
+
+ /**
+ * The adb connection is waiting for approval from the user.
+ */
+ AWAITING_USER_APPROVAL = 1;
+
+ /**
+ * The user allowed the adb connection from the system.
+ */
+ USER_ALLOWED = 2;
+
+ /**
+ * The user denied the adb connection from the system.
+ */
+ USER_DENIED = 3;
+
+ /**
+ * The adb connection was automatically allowed without user interaction due to the system
+ * being previously allowed by the user with the 'always allow' option selected, and the adb
+ * grant has not yet expired.
+ */
+ AUTOMATICALLY_ALLOWED = 4;
+
+ /**
+ * An empty or invalid base64 encoded key was provided to the framework; the connection was
+ * automatically denied.
+ */
+ DENIED_INVALID_KEY = 5;
+
+ /**
+ * vold decrypt has not yet occurred; the connection was automatically denied.
+ */
+ DENIED_VOLD_DECRYPT = 6;
+
+ /**
+ * The adb session has been disconnected.
+ */
+ DISCONNECTED = 7;
+}
+
diff --git a/core/proto/android/server/usagestatsservice.proto b/core/proto/android/server/usagestatsservice.proto
index 528c1a4..050ec7a 100644
--- a/core/proto/android/server/usagestatsservice.proto
+++ b/core/proto/android/server/usagestatsservice.proto
@@ -88,6 +88,11 @@
// If class field is an Activity, instance_id is a unique id of the
// Activity object.
optional int32 instance_id = 14;
+ // task_root_package_index contains the index + 1 of the task root package name in the string
+ // pool
+ optional int32 task_root_package_index = 15;
+ // task_root_class_index contains the index + 1 of the task root class name in the string pool
+ optional int32 task_root_class_index = 16;
}
// The following fields contain supplemental data used to build IntervalStats, such as a string
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 16d1d1a..1a21306 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -530,6 +530,9 @@
<!-- Boolean indicating whether the wifi chipset has dual frequency band support -->
<bool translatable="false" name="config_wifi_dual_band_support">false</bool>
+ <!-- Maximum number of concurrent WiFi interfaces in AP mode -->
+ <integer translatable="false" name="config_wifi_max_ap_interfaces">1</integer>
+
<!-- Boolean indicating whether the wifi chipset requires the softap band be -->
<!-- converted from 5GHz to ANY due to hardware restrictions -->
<bool translatable="false" name="config_wifi_convert_apband_5ghz_to_any">false</bool>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 7517218..83982c7 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1879,6 +1879,7 @@
<java-symbol type="bool" name="config_supportLongPressPowerWhenNonInteractive" />
<java-symbol type="bool" name="config_wifi_background_scan_support" />
<java-symbol type="bool" name="config_wifi_dual_band_support" />
+ <java-symbol type="integer" name="config_wifi_max_ap_interfaces" />
<java-symbol type="bool" name="config_wifi_convert_apband_5ghz_to_any" />
<java-symbol type="bool" name="config_wifi_local_only_hotspot_5ghz" />
<java-symbol type="bool" name="config_wifi_connected_mac_randomization_supported" />
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 87ad3d1..1431741 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -100,6 +100,7 @@
Settings.Global.ACTIVITY_MANAGER_CONSTANTS,
Settings.Global.ACTIVITY_STARTS_LOGGING_ENABLED,
Settings.Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED,
+ Settings.Global.ADB_ALLOWED_CONNECTION_TIME,
Settings.Global.ADB_ENABLED,
Settings.Global.ADD_USERS_WHEN_LOCKED,
Settings.Global.AIRPLANE_MODE_ON,
@@ -120,6 +121,7 @@
Settings.Global.APP_IDLE_CONSTANTS,
Settings.Global.APP_OPS_CONSTANTS,
Settings.Global.APP_STANDBY_ENABLED,
+ Settings.Global.APP_TIME_LIMIT_USAGE_SOURCE,
Settings.Global.ASSISTED_GPS_ENABLED,
Settings.Global.AUDIO_SAFE_VOLUME_STATE,
Settings.Global.AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES,
diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java
index e402055..c4ddd50 100644
--- a/graphics/java/android/graphics/HardwareRenderer.java
+++ b/graphics/java/android/graphics/HardwareRenderer.java
@@ -20,6 +20,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.TestApi;
import android.app.Activity;
import android.app.ActivityManager;
import android.os.IBinder;
@@ -667,6 +668,17 @@
nSetContentDrawBounds(mNativeProxy, left, top, right, bottom);
}
+ /** @hide */
+ public void setPictureCaptureCallback(@Nullable PictureCapturedCallback callback) {
+ nSetPictureCaptureCallback(mNativeProxy, callback);
+ }
+
+ /** called by native */
+ static void invokePictureCapturedCallback(long picturePtr, PictureCapturedCallback callback) {
+ Picture picture = new Picture(picturePtr);
+ callback.onPictureCaptured(picture);
+ }
+
/**
* Interface used to receive callbacks when a frame is being drawn.
*
@@ -695,6 +707,17 @@
void onFrameComplete(long frameNr);
}
+ /**
+ * Interface for listening to picture captures
+ * @hide
+ */
+ @TestApi
+ public interface PictureCapturedCallback {
+ /** @hide */
+ @TestApi
+ void onPictureCaptured(Picture picture);
+ }
+
private static void validateAlpha(float alpha, String argumentName) {
if (!(alpha >= 0.0f && alpha <= 1.0f)) {
throw new IllegalArgumentException(argumentName + " must be a valid alpha, "
@@ -998,6 +1021,9 @@
private static native void nSetContentDrawBounds(long nativeProxy, int left,
int top, int right, int bottom);
+ private static native void nSetPictureCaptureCallback(long nativeProxy,
+ PictureCapturedCallback callback);
+
private static native void nSetFrameCallback(long nativeProxy, FrameDrawingCallback callback);
private static native void nSetFrameCompleteCallback(long nativeProxy,
diff --git a/graphics/java/android/graphics/Picture.java b/graphics/java/android/graphics/Picture.java
index f6d801b..8d12cbf 100644
--- a/graphics/java/android/graphics/Picture.java
+++ b/graphics/java/android/graphics/Picture.java
@@ -16,6 +16,7 @@
package android.graphics;
+import android.annotation.NonNull;
import android.annotation.UnsupportedAppUsage;
import java.io.InputStream;
@@ -34,7 +35,8 @@
*/
public class Picture {
private PictureCanvas mRecordingCanvas;
- @UnsupportedAppUsage
+ // TODO: Figure out if this was a false-positive
+ @UnsupportedAppUsage(maxTargetSdk = 28)
private long mNativePicture;
private boolean mRequiresHwAcceleration;
@@ -56,23 +58,43 @@
this(nativeConstructor(src != null ? src.mNativePicture : 0));
}
- private Picture(long nativePicture) {
+ /** @hide */
+ public Picture(long nativePicture) {
if (nativePicture == 0) {
- throw new RuntimeException();
+ throw new IllegalArgumentException();
}
mNativePicture = nativePicture;
}
+ /**
+ * Immediately releases the backing data of the Picture. This object will no longer
+ * be usable after calling this, and any further calls on the Picture will throw an
+ * IllegalStateException.
+ * // TODO: Support?
+ * @hide
+ */
+ public void close() {
+ if (mNativePicture != 0) {
+ nativeDestructor(mNativePicture);
+ mNativePicture = 0;
+ }
+ }
+
@Override
protected void finalize() throws Throwable {
try {
- nativeDestructor(mNativePicture);
- mNativePicture = 0;
+ close();
} finally {
super.finalize();
}
}
+ private void verifyValid() {
+ if (mNativePicture == 0) {
+ throw new IllegalStateException("Picture is destroyed");
+ }
+ }
+
/**
* To record a picture, call beginRecording() and then draw into the Canvas
* that is returned. Nothing we appear on screen, but all of the draw
@@ -81,7 +103,9 @@
* that was returned must no longer be used, and nothing should be drawn
* into it.
*/
+ @NonNull
public Canvas beginRecording(int width, int height) {
+ verifyValid();
if (mRecordingCanvas != null) {
throw new IllegalStateException("Picture already recording, must call #endRecording()");
}
@@ -98,6 +122,7 @@
* or {@link Canvas#drawPicture(Picture)} is called.
*/
public void endRecording() {
+ verifyValid();
if (mRecordingCanvas != null) {
mRequiresHwAcceleration = mRecordingCanvas.mHoldsHwBitmap;
mRecordingCanvas = null;
@@ -110,7 +135,8 @@
* does not reflect (per se) the content of the picture.
*/
public int getWidth() {
- return nativeGetWidth(mNativePicture);
+ verifyValid();
+ return nativeGetWidth(mNativePicture);
}
/**
@@ -118,7 +144,8 @@
* does not reflect (per se) the content of the picture.
*/
public int getHeight() {
- return nativeGetHeight(mNativePicture);
+ verifyValid();
+ return nativeGetHeight(mNativePicture);
}
/**
@@ -133,6 +160,7 @@
* false otherwise.
*/
public boolean requiresHardwareAcceleration() {
+ verifyValid();
return mRequiresHwAcceleration;
}
@@ -149,7 +177,8 @@
*
* @param canvas The picture is drawn to this canvas
*/
- public void draw(Canvas canvas) {
+ public void draw(@NonNull Canvas canvas) {
+ verifyValid();
if (mRecordingCanvas != null) {
endRecording();
}
@@ -172,7 +201,7 @@
* raw or compressed pixels.
*/
@Deprecated
- public static Picture createFromStream(InputStream stream) {
+ public static Picture createFromStream(@NonNull InputStream stream) {
return new Picture(nativeCreateFromStream(stream, new byte[WORKING_STREAM_STORAGE]));
}
@@ -188,10 +217,11 @@
* Bitmap from which you can persist it as raw or compressed pixels.
*/
@Deprecated
- public void writeToStream(OutputStream stream) {
+ public void writeToStream(@NonNull OutputStream stream) {
+ verifyValid();
// do explicit check before calling the native method
if (stream == null) {
- throw new NullPointerException();
+ throw new IllegalArgumentException("stream cannot be null");
}
if (!nativeWriteToStream(mNativePicture, stream, new byte[WORKING_STREAM_STORAGE])) {
throw new RuntimeException();
diff --git a/graphics/java/android/graphics/RenderNode.java b/graphics/java/android/graphics/RenderNode.java
index 3b1d44b..09b18b7 100644
--- a/graphics/java/android/graphics/RenderNode.java
+++ b/graphics/java/android/graphics/RenderNode.java
@@ -184,7 +184,7 @@
*
* @param name The name of the RenderNode, used for debugging purpose. May be null.
*/
- public RenderNode(String name) {
+ public RenderNode(@Nullable String name) {
this(name, null);
}
diff --git a/graphics/proto/Android.bp b/graphics/proto/Android.bp
new file mode 100644
index 0000000..1d06348
--- /dev/null
+++ b/graphics/proto/Android.bp
@@ -0,0 +1,11 @@
+java_library_static {
+ name: "game-driver-protos",
+ host_supported: true,
+ proto: {
+ type: "lite",
+ },
+ srcs: ["game_driver.proto"],
+ no_framework_libs: true,
+ jarjar_rules: "jarjar-rules.txt",
+ sdk_version: "28",
+}
diff --git a/graphics/proto/game_driver.proto b/graphics/proto/game_driver.proto
new file mode 100644
index 0000000..fd7ffcc
--- /dev/null
+++ b/graphics/proto/game_driver.proto
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+package android.gamedriver;
+
+option java_package = "android.gamedriver";
+option java_outer_classname = "GameDriverProto";
+
+message Blacklist {
+ optional int64 version_code = 1;
+ repeated string package_names = 2;
+}
+
+message Blacklists {
+ repeated Blacklist blacklists = 1;
+}
diff --git a/graphics/proto/jarjar-rules.txt b/graphics/proto/jarjar-rules.txt
new file mode 100644
index 0000000..4e40637
--- /dev/null
+++ b/graphics/proto/jarjar-rules.txt
@@ -0,0 +1 @@
+rule com.google.protobuf.** com.android.framework.protobuf.@1
diff --git a/jarjar_rules_hidl.txt b/jarjar_rules_hidl.txt
new file mode 100644
index 0000000..4b2331d
--- /dev/null
+++ b/jarjar_rules_hidl.txt
@@ -0,0 +1 @@
+rule android.hidl.** android.internal.hidl.@1
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
index dd62bbb..7265692 100644
--- a/libs/hwui/VectorDrawable.cpp
+++ b/libs/hwui/VectorDrawable.cpp
@@ -551,6 +551,19 @@
SkPaint paint = inPaint;
paint.setAlpha(mProperties.getRootAlpha() * 255);
+ if (canvas->getGrContext() == nullptr) {
+ // Recording to picture, don't use the SkSurface which won't work off of renderthread.
+ Bitmap& bitmap = getBitmapUpdateIfDirty();
+ SkBitmap skiaBitmap;
+ bitmap.getSkBitmap(&skiaBitmap);
+
+ int scaledWidth = SkScalarCeilToInt(mProperties.getScaledWidth());
+ int scaledHeight = SkScalarCeilToInt(mProperties.getScaledHeight());
+ canvas->drawBitmapRect(skiaBitmap, SkRect::MakeWH(scaledWidth, scaledHeight), bounds,
+ &paint, SkCanvas::kFast_SrcRectConstraint);
+ return;
+ }
+
SkRect src;
sk_sp<SkSurface> vdSurface = mCache.getSurface(&src);
if (vdSurface) {
diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
index 240efb4..60c8057 100644
--- a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
@@ -74,7 +74,13 @@
void GLFunctorDrawable::onDraw(SkCanvas* canvas) {
if (canvas->getGrContext() == nullptr) {
- SkDEBUGF(("Attempting to draw GLFunctor into an unsupported surface"));
+ // We're dumping a picture, render a light-blue rectangle instead
+ // TODO: Draw the WebView text on top? Seemingly complicated as SkPaint doesn't
+ // seem to have a default typeface that works. We only ever use drawGlyphs, which
+ // requires going through minikin & hwui's canvas which we don't have here.
+ SkPaint paint;
+ paint.setColor(0xFF81D4FA);
+ canvas->drawRect(mBounds, paint);
return;
}
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index df82243..47c9094 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -111,7 +111,7 @@
const Rect& layerDamage = layers.entries()[i].damage;
- SkCanvas* layerCanvas = tryCapture(layerNode->getLayerSurface());
+ SkCanvas* layerCanvas = layerNode->getLayerSurface()->getCanvas();
int saveCount = layerCanvas->save();
SkASSERT(saveCount == 1);
@@ -139,8 +139,6 @@
layerCanvas->restoreToCount(saveCount);
mLightCenter = savedLightCenter;
- endCapture(layerNode->getLayerSurface());
-
// cache the current context so that we can defer flushing it until
// either all the layers have been rendered or the context changes
GrContext* currentContext = layerNode->getLayerSurface()->getCanvas()->getGrContext();
@@ -244,6 +242,7 @@
}
virtual void onProcess(const sp<Task<bool>>& task) override {
+ ATRACE_NAME("SavePictureTask");
SavePictureTask* t = static_cast<SavePictureTask*>(task.get());
if (0 == access(t->filename.c_str(), F_OK)) {
@@ -265,46 +264,56 @@
SkCanvas* SkiaPipeline::tryCapture(SkSurface* surface) {
if (CC_UNLIKELY(Properties::skpCaptureEnabled)) {
- bool recordingPicture = mCaptureSequence > 0;
char prop[PROPERTY_VALUE_MAX] = {'\0'};
- if (!recordingPicture) {
+ if (mCaptureSequence <= 0) {
property_get(PROPERTY_CAPTURE_SKP_FILENAME, prop, "0");
- recordingPicture = prop[0] != '0' &&
- mCapturedFile != prop; // ensure we capture only once per filename
- if (recordingPicture) {
+ if (prop[0] != '0' && mCapturedFile != prop) {
mCapturedFile = prop;
mCaptureSequence = property_get_int32(PROPERTY_CAPTURE_SKP_FRAMES, 1);
}
}
- if (recordingPicture) {
+ if (mCaptureSequence > 0 || mPictureCapturedCallback) {
mRecorder.reset(new SkPictureRecorder());
- return mRecorder->beginRecording(surface->width(), surface->height(), nullptr,
- SkPictureRecorder::kPlaybackDrawPicture_RecordFlag);
+ SkCanvas* pictureCanvas = mRecorder->beginRecording(surface->width(), surface->height(), nullptr,
+ SkPictureRecorder::kPlaybackDrawPicture_RecordFlag);
+ mNwayCanvas = std::make_unique<SkNWayCanvas>(surface->width(), surface->height());
+ mNwayCanvas->addCanvas(surface->getCanvas());
+ mNwayCanvas->addCanvas(pictureCanvas);
+ return mNwayCanvas.get();
}
}
return surface->getCanvas();
}
void SkiaPipeline::endCapture(SkSurface* surface) {
+ mNwayCanvas.reset();
if (CC_UNLIKELY(mRecorder.get())) {
+ ATRACE_CALL();
sk_sp<SkPicture> picture = mRecorder->finishRecordingAsPicture();
- surface->getCanvas()->drawPicture(picture);
if (picture->approximateOpCount() > 0) {
- auto data = picture->serialize();
+ if (mCaptureSequence > 0) {
+ ATRACE_BEGIN("picture->serialize");
+ auto data = picture->serialize();
+ ATRACE_END();
- // offload saving to file in a different thread
- if (!mSavePictureProcessor.get()) {
- TaskManager* taskManager = getTaskManager();
- mSavePictureProcessor = new SavePictureProcessor(
- taskManager->canRunTasks() ? taskManager : nullptr);
+ // offload saving to file in a different thread
+ if (!mSavePictureProcessor.get()) {
+ TaskManager* taskManager = getTaskManager();
+ mSavePictureProcessor = new SavePictureProcessor(
+ taskManager->canRunTasks() ? taskManager : nullptr);
+ }
+ if (1 == mCaptureSequence) {
+ mSavePictureProcessor->savePicture(data, mCapturedFile);
+ } else {
+ mSavePictureProcessor->savePicture(
+ data,
+ mCapturedFile + "_" + std::to_string(mCaptureSequence));
+ }
+ mCaptureSequence--;
}
- if (1 == mCaptureSequence) {
- mSavePictureProcessor->savePicture(data, mCapturedFile);
- } else {
- mSavePictureProcessor->savePicture(
- data, mCapturedFile + "_" + std::to_string(mCaptureSequence));
+ if (mPictureCapturedCallback) {
+ std::invoke(mPictureCapturedCallback, std::move(picture));
}
- mCaptureSequence--;
}
mRecorder.reset();
}
@@ -314,6 +323,11 @@
const std::vector<sp<RenderNode>>& nodes, bool opaque,
const Rect& contentDrawBounds, sk_sp<SkSurface> surface,
const SkMatrix& preTransform) {
+ bool previousSkpEnabled = Properties::skpCaptureEnabled;
+ if (mPictureCapturedCallback) {
+ Properties::skpCaptureEnabled = true;
+ }
+
renderVectorDrawableCache();
// draw all layers up front
@@ -334,6 +348,8 @@
ATRACE_NAME("flush commands");
surface->getCanvas()->flush();
+
+ Properties::skpCaptureEnabled = previousSkpEnabled;
}
namespace {
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h
index cf6f5b2..e9957df 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.h
@@ -105,6 +105,11 @@
mLightCenter = lightGeometry.center;
}
+ void setPictureCapturedCallback(
+ const std::function<void(sk_sp<SkPicture>&&)>& callback) override {
+ mPictureCapturedCallback = callback;
+ }
+
protected:
void dumpResourceCacheUsage() const;
void setSurfaceColorProperties(renderthread::ColorMode colorMode);
@@ -163,6 +168,8 @@
* parallel tryCapture calls (not really needed).
*/
std::unique_ptr<SkPictureRecorder> mRecorder;
+ std::unique_ptr<SkNWayCanvas> mNwayCanvas;
+ std::function<void(sk_sp<SkPicture>&&)> mPictureCapturedCallback;
static float mLightRadius;
static uint8_t mAmbientShadowAlpha;
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 9e7abf4..db97763 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -184,6 +184,10 @@
mFrameCompleteCallbacks.push_back(std::move(func));
}
+ void setPictureCapturedCallback(const std::function<void(sk_sp<SkPicture>&&)>& callback) {
+ mRenderPipeline->setPictureCapturedCallback(callback);
+ }
+
void setForceDark(bool enable) {
mUseForceDark = enable;
}
diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h
index d4dd629..2cfc8df3 100644
--- a/libs/hwui/renderthread/IRenderPipeline.h
+++ b/libs/hwui/renderthread/IRenderPipeline.h
@@ -59,15 +59,15 @@
virtual MakeCurrentResult makeCurrent() = 0;
virtual Frame getFrame() = 0;
virtual bool draw(const Frame& frame, const SkRect& screenDirty, const SkRect& dirty,
- const LightGeometry& lightGeometry,
- LayerUpdateQueue* layerUpdateQueue, const Rect& contentDrawBounds,
- bool opaque, const LightInfo& lightInfo,
+ const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue,
+ const Rect& contentDrawBounds, bool opaque, const LightInfo& lightInfo,
const std::vector<sp<RenderNode>>& renderNodes,
FrameInfoVisualizer* profiler) = 0;
virtual bool swapBuffers(const Frame& frame, bool drew, const SkRect& screenDirty,
FrameInfo* currentFrameInfo, bool* requireSwap) = 0;
virtual DeferredLayerUpdater* createTextureLayer() = 0;
- virtual bool setSurface(ANativeWindow* window, SwapBehavior swapBehavior, ColorMode colorMode) = 0;
+ virtual bool setSurface(ANativeWindow* window, SwapBehavior swapBehavior,
+ ColorMode colorMode) = 0;
virtual void onStop() = 0;
virtual bool isSurfaceReady() = 0;
virtual bool isContextReady() = 0;
@@ -85,6 +85,8 @@
virtual SkColorType getSurfaceColorType() const = 0;
virtual sk_sp<SkColorSpace> getSurfaceColorSpace() = 0;
virtual GrSurfaceOrigin getSurfaceOrigin() = 0;
+ virtual void setPictureCapturedCallback(
+ const std::function<void(sk_sp<SkPicture>&&)>& callback) = 0;
virtual ~IRenderPipeline() {}
};
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index aa6af23..ab59af7 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -21,6 +21,7 @@
#include "Properties.h"
#include "Readback.h"
#include "Rect.h"
+#include "WebViewFunctorManager.h"
#include "pipeline/skia/SkiaOpenGLPipeline.h"
#include "pipeline/skia/VectorDrawableAtlas.h"
#include "renderstate/RenderState.h"
@@ -30,7 +31,6 @@
#include "renderthread/RenderThread.h"
#include "utils/Macros.h"
#include "utils/TimeUtils.h"
-#include "WebViewFunctorManager.h"
#include <ui/GraphicBuffer.h>
@@ -147,9 +147,7 @@
void RenderProxy::destroyFunctor(int functor) {
ATRACE_CALL();
RenderThread& thread = RenderThread::getInstance();
- thread.queue().post([=]() {
- WebViewFunctorManager::instance().destroyFunctor(functor);
- });
+ thread.queue().post([=]() { WebViewFunctorManager::instance().destroyFunctor(functor); });
}
DeferredLayerUpdater* RenderProxy::createTextureLayer() {
@@ -164,9 +162,9 @@
bool RenderProxy::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap& bitmap) {
auto& thread = RenderThread::getInstance();
- return thread.queue().runSync(
- [&]() -> bool { return thread.readback().copyLayerInto(layer, &bitmap)
- == CopyResult::Success; });
+ return thread.queue().runSync([&]() -> bool {
+ return thread.readback().copyLayerInto(layer, &bitmap) == CopyResult::Success;
+ });
}
void RenderProxy::pushLayerUpdate(DeferredLayerUpdater* layer) {
@@ -204,9 +202,8 @@
}
int RenderProxy::maxTextureSize() {
- static int maxTextureSize = RenderThread::getInstance().queue().runSync([]() {
- return DeviceInfo::get()->maxTextureSize();
- });
+ static int maxTextureSize = RenderThread::getInstance().queue().runSync(
+ []() { return DeviceInfo::get()->maxTextureSize(); });
return maxTextureSize;
}
@@ -281,6 +278,12 @@
mDrawFrameTask.setContentDrawBounds(left, top, right, bottom);
}
+void RenderProxy::setPictureCapturedCallback(
+ const std::function<void(sk_sp<SkPicture>&&)>& callback) {
+ mRenderThread.queue().post(
+ [ this, cb = callback ]() { mContext->setPictureCapturedCallback(cb); });
+}
+
void RenderProxy::setFrameCallback(std::function<void(int64_t)>&& callback) {
mDrawFrameTask.setFrameCallback(std::move(callback));
}
@@ -302,9 +305,7 @@
}
void RenderProxy::setForceDark(bool enable) {
- mRenderThread.queue().post([this, enable]() {
- mContext->setForceDark(enable);
- });
+ mRenderThread.queue().post([this, enable]() { mContext->setForceDark(enable); });
}
int RenderProxy::copySurfaceInto(sp<Surface>& surface, int left, int top, int right, int bottom,
@@ -348,9 +349,8 @@
// TODO: fix everything that hits this. We should never be triggering a readback ourselves.
return (int)thread.readback().copyHWBitmapInto(hwBitmap, bitmap);
} else {
- return thread.queue().runSync([&]() -> int {
- return (int)thread.readback().copyHWBitmapInto(hwBitmap, bitmap);
- });
+ return thread.queue().runSync(
+ [&]() -> int { return (int)thread.readback().copyHWBitmapInto(hwBitmap, bitmap); });
}
}
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 9dc9181..6e1bfd7 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -114,6 +114,8 @@
ANDROID_API void removeRenderNode(RenderNode* node);
ANDROID_API void drawRenderNode(RenderNode* node);
ANDROID_API void setContentDrawBounds(int left, int top, int right, int bottom);
+ ANDROID_API void setPictureCapturedCallback(
+ const std::function<void(sk_sp<SkPicture>&&)>& callback);
ANDROID_API void setFrameCallback(std::function<void(int64_t)>&& callback);
ANDROID_API void setFrameCompleteCallback(std::function<void(int64_t)>&& callback);
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index bfc10da5..2d2c4a8 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -176,7 +176,8 @@
* @param uuid The UUID of the crypto scheme.
*/
public static final boolean isCryptoSchemeSupported(@NonNull UUID uuid) {
- return isCryptoSchemeSupportedNative(getByteArrayFromUUID(uuid), null);
+ return isCryptoSchemeSupportedNative(getByteArrayFromUUID(uuid), null,
+ SECURITY_LEVEL_UNKNOWN);
}
/**
@@ -189,7 +190,25 @@
*/
public static final boolean isCryptoSchemeSupported(
@NonNull UUID uuid, @NonNull String mimeType) {
- return isCryptoSchemeSupportedNative(getByteArrayFromUUID(uuid), mimeType);
+ return isCryptoSchemeSupportedNative(getByteArrayFromUUID(uuid),
+ mimeType, SECURITY_LEVEL_UNKNOWN);
+ }
+
+ /**
+ * Query if the given scheme identified by its UUID is supported on
+ * this device, and whether the DRM plugin is able to handle the
+ * media container format specified by mimeType at the requested
+ * security level.
+ *
+ * @param uuid The UUID of the crypto scheme.
+ * @param mimeType The MIME type of the media container, e.g. "video/mp4"
+ * or "video/webm"
+ * @param securityLevel the security level requested
+ */
+ public static final boolean isCryptoSchemeSupported(
+ @NonNull UUID uuid, @NonNull String mimeType, @SecurityLevel int securityLevel) {
+ return isCryptoSchemeSupportedNative(getByteArrayFromUUID(uuid), mimeType,
+ securityLevel);
}
private static final byte[] getByteArrayFromUUID(@NonNull UUID uuid) {
@@ -206,7 +225,7 @@
}
private static final native boolean isCryptoSchemeSupportedNative(
- @NonNull byte[] uuid, @Nullable String mimeType);
+ @NonNull byte[] uuid, @Nullable String mimeType, @SecurityLevel int securityLevel);
private EventHandler createHandler() {
Looper looper;
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index 42c5b05..81fce8a 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -542,14 +542,15 @@
// static
-bool JDrm::IsCryptoSchemeSupported(const uint8_t uuid[16], const String8 &mimeType) {
+bool JDrm::IsCryptoSchemeSupported(const uint8_t uuid[16], const String8 &mimeType,
+ DrmPlugin::SecurityLevel securityLevel) {
sp<IDrm> drm = MakeDrm();
if (drm == NULL) {
return false;
}
- return drm->isCryptoSchemeSupported(uuid, mimeType);
+ return drm->isCryptoSchemeSupported(uuid, mimeType, securityLevel);
}
status_t JDrm::initCheck() const {
@@ -930,8 +931,30 @@
setDrm(env, thiz, drm);
}
+DrmPlugin::SecurityLevel jintToSecurityLevel(jint jlevel) {
+ DrmPlugin::SecurityLevel level;
+
+ if (jlevel == gSecurityLevels.kSecurityLevelMax) {
+ level = DrmPlugin::kSecurityLevelMax;
+ } else if (jlevel == gSecurityLevels.kSecurityLevelSwSecureCrypto) {
+ level = DrmPlugin::kSecurityLevelSwSecureCrypto;
+ } else if (jlevel == gSecurityLevels.kSecurityLevelSwSecureDecode) {
+ level = DrmPlugin::kSecurityLevelSwSecureDecode;
+ } else if (jlevel == gSecurityLevels.kSecurityLevelHwSecureCrypto) {
+ level = DrmPlugin::kSecurityLevelHwSecureCrypto;
+ } else if (jlevel == gSecurityLevels.kSecurityLevelHwSecureDecode) {
+ level = DrmPlugin::kSecurityLevelHwSecureDecode;
+ } else if (jlevel == gSecurityLevels.kSecurityLevelHwSecureAll) {
+ level = DrmPlugin::kSecurityLevelHwSecureAll;
+ } else {
+ level = DrmPlugin::kSecurityLevelUnknown;
+ }
+ return level;
+}
+
static jboolean android_media_MediaDrm_isCryptoSchemeSupportedNative(
- JNIEnv *env, jobject /* thiz */, jbyteArray uuidObj, jstring jmimeType) {
+ JNIEnv *env, jobject /* thiz */, jbyteArray uuidObj, jstring jmimeType,
+ jint jSecurityLevel) {
if (uuidObj == NULL) {
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
@@ -952,8 +975,9 @@
if (jmimeType != NULL) {
mimeType = JStringToString8(env, jmimeType);
}
+ DrmPlugin::SecurityLevel securityLevel = jintToSecurityLevel(jSecurityLevel);
- return JDrm::IsCryptoSchemeSupported(uuid.array(), mimeType);
+ return JDrm::IsCryptoSchemeSupported(uuid.array(), mimeType, securityLevel);
}
static jbyteArray android_media_MediaDrm_openSession(
@@ -965,21 +989,8 @@
}
Vector<uint8_t> sessionId;
- DrmPlugin::SecurityLevel level;
-
- if (jlevel == gSecurityLevels.kSecurityLevelMax) {
- level = DrmPlugin::kSecurityLevelMax;
- } else if (jlevel == gSecurityLevels.kSecurityLevelSwSecureCrypto) {
- level = DrmPlugin::kSecurityLevelSwSecureCrypto;
- } else if (jlevel == gSecurityLevels.kSecurityLevelSwSecureDecode) {
- level = DrmPlugin::kSecurityLevelSwSecureDecode;
- } else if (jlevel == gSecurityLevels.kSecurityLevelHwSecureCrypto) {
- level = DrmPlugin::kSecurityLevelHwSecureCrypto;
- } else if (jlevel == gSecurityLevels.kSecurityLevelHwSecureDecode) {
- level = DrmPlugin::kSecurityLevelHwSecureDecode;
- } else if (jlevel == gSecurityLevels.kSecurityLevelHwSecureAll) {
- level = DrmPlugin::kSecurityLevelHwSecureAll;
- } else {
+ DrmPlugin::SecurityLevel level = jintToSecurityLevel(jlevel);
+ if (level == DrmPlugin::kSecurityLevelUnknown) {
jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid security level");
return NULL;
}
@@ -1903,7 +1914,7 @@
{ "native_setup", "(Ljava/lang/Object;[BLjava/lang/String;)V",
(void *)android_media_MediaDrm_native_setup },
- { "isCryptoSchemeSupportedNative", "([BLjava/lang/String;)Z",
+ { "isCryptoSchemeSupportedNative", "([BLjava/lang/String;I)Z",
(void *)android_media_MediaDrm_isCryptoSchemeSupportedNative },
{ "openSession", "(I)[B",
diff --git a/media/jni/android_media_MediaDrm.h b/media/jni/android_media_MediaDrm.h
index b9356f3..9338861 100644
--- a/media/jni/android_media_MediaDrm.h
+++ b/media/jni/android_media_MediaDrm.h
@@ -37,7 +37,9 @@
};
struct JDrm : public BnDrmClient {
- static bool IsCryptoSchemeSupported(const uint8_t uuid[16], const String8 &mimeType);
+ static bool IsCryptoSchemeSupported(const uint8_t uuid[16],
+ const String8 &mimeType,
+ DrmPlugin::SecurityLevel level);
JDrm(JNIEnv *env, jobject thiz, const uint8_t uuid[16], const String8 &appPackageName);
diff --git a/packages/NetworkStack/Android.bp b/packages/NetworkStack/Android.bp
index 2f7d599..a2da0a0 100644
--- a/packages/NetworkStack/Android.bp
+++ b/packages/NetworkStack/Android.bp
@@ -24,7 +24,7 @@
":services-networkstack-shared-srcs",
],
static_libs: [
- "dhcp-packet-lib",
+ "services-netlink-lib",
]
}
diff --git a/packages/NetworkStack/AndroidManifest.xml b/packages/NetworkStack/AndroidManifest.xml
index 7f8bb93..5ab833b 100644
--- a/packages/NetworkStack/AndroidManifest.xml
+++ b/packages/NetworkStack/AndroidManifest.xml
@@ -28,6 +28,7 @@
<!-- Launch captive portal app as specific user -->
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
<uses-permission android:name="android.permission.NETWORK_STACK" />
+ <uses-permission android:name="android.permission.WAKE_LOCK" />
<application
android:label="NetworkStack"
android:defaultToDeviceProtectedStorage="true"
diff --git a/services/net/java/android/net/apf/ApfFilter.java b/packages/NetworkStack/src/android/net/apf/ApfFilter.java
similarity index 98%
rename from services/net/java/android/net/apf/ApfFilter.java
rename to packages/NetworkStack/src/android/net/apf/ApfFilter.java
index 4943952..50c4dfc 100644
--- a/services/net/java/android/net/apf/ApfFilter.java
+++ b/packages/NetworkStack/src/android/net/apf/ApfFilter.java
@@ -16,10 +16,6 @@
package android.net.apf;
-import static android.net.util.NetworkConstants.ICMPV6_ECHO_REQUEST_TYPE;
-import static android.net.util.NetworkConstants.ICMPV6_NEIGHBOR_ADVERTISEMENT;
-import static android.net.util.NetworkConstants.ICMPV6_ROUTER_ADVERTISEMENT;
-import static android.net.util.NetworkConstants.ICMPV6_ROUTER_SOLICITATION;
import static android.net.util.SocketUtils.makePacketSocketAddress;
import static android.system.OsConstants.AF_PACKET;
import static android.system.OsConstants.ARPHRD_ETHER;
@@ -35,6 +31,10 @@
import static com.android.internal.util.BitUtils.getUint32;
import static com.android.internal.util.BitUtils.getUint8;
import static com.android.internal.util.BitUtils.uint32;
+import static com.android.server.util.NetworkStackConstants.ICMPV6_ECHO_REQUEST_TYPE;
+import static com.android.server.util.NetworkStackConstants.ICMPV6_NEIGHBOR_ADVERTISEMENT;
+import static com.android.server.util.NetworkStackConstants.ICMPV6_ROUTER_ADVERTISEMENT;
+import static com.android.server.util.NetworkStackConstants.ICMPV6_ROUTER_SOLICITATION;
import android.annotation.Nullable;
import android.content.BroadcastReceiver;
@@ -46,7 +46,7 @@
import android.net.NetworkUtils;
import android.net.apf.ApfGenerator.IllegalInstructionException;
import android.net.apf.ApfGenerator.Register;
-import android.net.ip.IpClientCallbacks;
+import android.net.ip.IpClient.IpClientCallbacksWrapper;
import android.net.metrics.ApfProgramEvent;
import android.net.metrics.ApfStats;
import android.net.metrics.IpConnectivityLog;
@@ -337,7 +337,7 @@
private static final int APF_MAX_ETH_TYPE_BLACK_LIST_LEN = 20;
private final ApfCapabilities mApfCapabilities;
- private final IpClientCallbacks mIpClientCallback;
+ private final IpClientCallbacksWrapper mIpClientCallback;
private final InterfaceParams mInterfaceParams;
private final IpConnectivityLog mMetricsLog;
@@ -378,7 +378,7 @@
@VisibleForTesting
ApfFilter(Context context, ApfConfiguration config, InterfaceParams ifParams,
- IpClientCallbacks ipClientCallback, IpConnectivityLog log) {
+ IpClientCallbacksWrapper ipClientCallback, IpConnectivityLog log) {
mApfCapabilities = config.apfCapabilities;
mIpClientCallback = ipClientCallback;
mInterfaceParams = ifParams;
@@ -1420,7 +1420,7 @@
* filtering using APF programs.
*/
public static ApfFilter maybeCreate(Context context, ApfConfiguration config,
- InterfaceParams ifParams, IpClientCallbacks ipClientCallback) {
+ InterfaceParams ifParams, IpClientCallbacksWrapper ipClientCallback) {
if (context == null || config == null || ifParams == null) return null;
ApfCapabilities apfCapabilities = config.apfCapabilities;
if (apfCapabilities == null) return null;
diff --git a/services/net/java/android/net/apf/ApfGenerator.java b/packages/NetworkStack/src/android/net/apf/ApfGenerator.java
similarity index 100%
rename from services/net/java/android/net/apf/ApfGenerator.java
rename to packages/NetworkStack/src/android/net/apf/ApfGenerator.java
diff --git a/services/net/java/android/net/dhcp/DhcpAckPacket.java b/packages/NetworkStack/src/android/net/dhcp/DhcpAckPacket.java
similarity index 100%
rename from services/net/java/android/net/dhcp/DhcpAckPacket.java
rename to packages/NetworkStack/src/android/net/dhcp/DhcpAckPacket.java
diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpClient.java b/packages/NetworkStack/src/android/net/dhcp/DhcpClient.java
new file mode 100644
index 0000000..04ac9a3
--- /dev/null
+++ b/packages/NetworkStack/src/android/net/dhcp/DhcpClient.java
@@ -0,0 +1,1052 @@
+/*
+ * Copyright (C) 2015 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.net.dhcp;
+
+import static android.net.dhcp.DhcpPacket.DHCP_BROADCAST_ADDRESS;
+import static android.net.dhcp.DhcpPacket.DHCP_DNS_SERVER;
+import static android.net.dhcp.DhcpPacket.DHCP_DOMAIN_NAME;
+import static android.net.dhcp.DhcpPacket.DHCP_LEASE_TIME;
+import static android.net.dhcp.DhcpPacket.DHCP_MTU;
+import static android.net.dhcp.DhcpPacket.DHCP_REBINDING_TIME;
+import static android.net.dhcp.DhcpPacket.DHCP_RENEWAL_TIME;
+import static android.net.dhcp.DhcpPacket.DHCP_ROUTER;
+import static android.net.dhcp.DhcpPacket.DHCP_SUBNET_MASK;
+import static android.net.dhcp.DhcpPacket.DHCP_VENDOR_INFO;
+import static android.net.dhcp.DhcpPacket.INADDR_ANY;
+import static android.net.dhcp.DhcpPacket.INADDR_BROADCAST;
+import static android.net.util.SocketUtils.makePacketSocketAddress;
+import static android.system.OsConstants.AF_INET;
+import static android.system.OsConstants.AF_PACKET;
+import static android.system.OsConstants.ETH_P_IP;
+import static android.system.OsConstants.IPPROTO_UDP;
+import static android.system.OsConstants.SOCK_DGRAM;
+import static android.system.OsConstants.SOCK_RAW;
+import static android.system.OsConstants.SOL_SOCKET;
+import static android.system.OsConstants.SO_BROADCAST;
+import static android.system.OsConstants.SO_RCVBUF;
+import static android.system.OsConstants.SO_REUSEADDR;
+
+import android.content.Context;
+import android.net.DhcpResults;
+import android.net.NetworkUtils;
+import android.net.TrafficStats;
+import android.net.ip.IpClient;
+import android.net.metrics.DhcpClientEvent;
+import android.net.metrics.DhcpErrorEvent;
+import android.net.metrics.IpConnectivityLog;
+import android.net.util.InterfaceParams;
+import android.net.util.SocketUtils;
+import android.os.Message;
+import android.os.SystemClock;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.util.EventLog;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.internal.util.HexDump;
+import com.android.internal.util.MessageUtils;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
+import com.android.internal.util.WakeupMessage;
+
+import libcore.io.IoBridge;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.net.Inet4Address;
+import java.net.SocketAddress;
+import java.net.SocketException;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.Random;
+
+/**
+ * A DHCPv4 client.
+ *
+ * Written to behave similarly to the DhcpStateMachine + dhcpcd 5.5.6 combination used in Android
+ * 5.1 and below, as configured on Nexus 6. The interface is the same as DhcpStateMachine.
+ *
+ * TODO:
+ *
+ * - Exponential backoff when receiving NAKs (not specified by the RFC, but current behaviour).
+ * - Support persisting lease state and support INIT-REBOOT. Android 5.1 does this, but it does not
+ * do so correctly: instead of requesting the lease last obtained on a particular network (e.g., a
+ * given SSID), it requests the last-leased IP address on the same interface, causing a delay if
+ * the server NAKs or a timeout if it doesn't.
+ *
+ * Known differences from current behaviour:
+ *
+ * - Does not request the "static routes" option.
+ * - Does not support BOOTP servers. DHCP has been around since 1993, should be everywhere now.
+ * - Requests the "broadcast" option, but does nothing with it.
+ * - Rejects invalid subnet masks such as 255.255.255.1 (current code treats that as 255.255.255.0).
+ *
+ * @hide
+ */
+public class DhcpClient extends StateMachine {
+
+ private static final String TAG = "DhcpClient";
+ private static final boolean DBG = true;
+ private static final boolean STATE_DBG = false;
+ private static final boolean MSG_DBG = false;
+ private static final boolean PACKET_DBG = false;
+
+ // Timers and timeouts.
+ private static final int SECONDS = 1000;
+ private static final int FIRST_TIMEOUT_MS = 2 * SECONDS;
+ private static final int MAX_TIMEOUT_MS = 128 * SECONDS;
+
+ // This is not strictly needed, since the client is asynchronous and implements exponential
+ // backoff. It's maintained for backwards compatibility with the previous DHCP code, which was
+ // a blocking operation with a 30-second timeout. We pick 36 seconds so we can send packets at
+ // t=0, t=2, t=6, t=14, t=30, allowing for 10% jitter.
+ private static final int DHCP_TIMEOUT_MS = 36 * SECONDS;
+
+ // DhcpClient uses IpClient's handler.
+ private static final int PUBLIC_BASE = IpClient.DHCPCLIENT_CMD_BASE;
+
+ /* Commands from controller to start/stop DHCP */
+ public static final int CMD_START_DHCP = PUBLIC_BASE + 1;
+ public static final int CMD_STOP_DHCP = PUBLIC_BASE + 2;
+
+ /* Notification from DHCP state machine prior to DHCP discovery/renewal */
+ public static final int CMD_PRE_DHCP_ACTION = PUBLIC_BASE + 3;
+ /* Notification from DHCP state machine post DHCP discovery/renewal. Indicates
+ * success/failure */
+ public static final int CMD_POST_DHCP_ACTION = PUBLIC_BASE + 4;
+ /* Notification from DHCP state machine before quitting */
+ public static final int CMD_ON_QUIT = PUBLIC_BASE + 5;
+
+ /* Command from controller to indicate DHCP discovery/renewal can continue
+ * after pre DHCP action is complete */
+ public static final int CMD_PRE_DHCP_ACTION_COMPLETE = PUBLIC_BASE + 6;
+
+ /* Command and event notification to/from IpManager requesting the setting
+ * (or clearing) of an IPv4 LinkAddress.
+ */
+ public static final int CMD_CLEAR_LINKADDRESS = PUBLIC_BASE + 7;
+ public static final int CMD_CONFIGURE_LINKADDRESS = PUBLIC_BASE + 8;
+ public static final int EVENT_LINKADDRESS_CONFIGURED = PUBLIC_BASE + 9;
+
+ /* Message.arg1 arguments to CMD_POST_DHCP_ACTION notification */
+ public static final int DHCP_SUCCESS = 1;
+ public static final int DHCP_FAILURE = 2;
+
+ // Internal messages.
+ private static final int PRIVATE_BASE = IpClient.DHCPCLIENT_CMD_BASE + 100;
+ private static final int CMD_KICK = PRIVATE_BASE + 1;
+ private static final int CMD_RECEIVED_PACKET = PRIVATE_BASE + 2;
+ private static final int CMD_TIMEOUT = PRIVATE_BASE + 3;
+ private static final int CMD_RENEW_DHCP = PRIVATE_BASE + 4;
+ private static final int CMD_REBIND_DHCP = PRIVATE_BASE + 5;
+ private static final int CMD_EXPIRE_DHCP = PRIVATE_BASE + 6;
+
+ // For message logging.
+ private static final Class[] sMessageClasses = { DhcpClient.class };
+ private static final SparseArray<String> sMessageNames =
+ MessageUtils.findMessageNames(sMessageClasses);
+
+ // DHCP parameters that we request.
+ /* package */ static final byte[] REQUESTED_PARAMS = new byte[] {
+ DHCP_SUBNET_MASK,
+ DHCP_ROUTER,
+ DHCP_DNS_SERVER,
+ DHCP_DOMAIN_NAME,
+ DHCP_MTU,
+ DHCP_BROADCAST_ADDRESS, // TODO: currently ignored.
+ DHCP_LEASE_TIME,
+ DHCP_RENEWAL_TIME,
+ DHCP_REBINDING_TIME,
+ DHCP_VENDOR_INFO,
+ };
+
+ // DHCP flag that means "yes, we support unicast."
+ private static final boolean DO_UNICAST = false;
+
+ // System services / libraries we use.
+ private final Context mContext;
+ private final Random mRandom;
+ private final IpConnectivityLog mMetricsLog = new IpConnectivityLog();
+
+ // Sockets.
+ // - We use a packet socket to receive, because servers send us packets bound for IP addresses
+ // which we have not yet configured, and the kernel protocol stack drops these.
+ // - We use a UDP socket to send, so the kernel handles ARP and routing for us (DHCP servers can
+ // be off-link as well as on-link).
+ private FileDescriptor mPacketSock;
+ private FileDescriptor mUdpSock;
+ private ReceiveThread mReceiveThread;
+
+ // State variables.
+ private final StateMachine mController;
+ private final WakeupMessage mKickAlarm;
+ private final WakeupMessage mTimeoutAlarm;
+ private final WakeupMessage mRenewAlarm;
+ private final WakeupMessage mRebindAlarm;
+ private final WakeupMessage mExpiryAlarm;
+ private final String mIfaceName;
+
+ private boolean mRegisteredForPreDhcpNotification;
+ private InterfaceParams mIface;
+ // TODO: MacAddress-ify more of this class hierarchy.
+ private byte[] mHwAddr;
+ private SocketAddress mInterfaceBroadcastAddr;
+ private int mTransactionId;
+ private long mTransactionStartMillis;
+ private DhcpResults mDhcpLease;
+ private long mDhcpLeaseExpiry;
+ private DhcpResults mOffer;
+
+ // Milliseconds SystemClock timestamps used to record transition times to DhcpBoundState.
+ private long mLastInitEnterTime;
+ private long mLastBoundExitTime;
+
+ // States.
+ private State mStoppedState = new StoppedState();
+ private State mDhcpState = new DhcpState();
+ private State mDhcpInitState = new DhcpInitState();
+ private State mDhcpSelectingState = new DhcpSelectingState();
+ private State mDhcpRequestingState = new DhcpRequestingState();
+ private State mDhcpHaveLeaseState = new DhcpHaveLeaseState();
+ private State mConfiguringInterfaceState = new ConfiguringInterfaceState();
+ private State mDhcpBoundState = new DhcpBoundState();
+ private State mDhcpRenewingState = new DhcpRenewingState();
+ private State mDhcpRebindingState = new DhcpRebindingState();
+ private State mDhcpInitRebootState = new DhcpInitRebootState();
+ private State mDhcpRebootingState = new DhcpRebootingState();
+ private State mWaitBeforeStartState = new WaitBeforeStartState(mDhcpInitState);
+ private State mWaitBeforeRenewalState = new WaitBeforeRenewalState(mDhcpRenewingState);
+
+ private WakeupMessage makeWakeupMessage(String cmdName, int cmd) {
+ cmdName = DhcpClient.class.getSimpleName() + "." + mIfaceName + "." + cmdName;
+ return new WakeupMessage(mContext, getHandler(), cmdName, cmd);
+ }
+
+ // TODO: Take an InterfaceParams instance instead of an interface name String.
+ private DhcpClient(Context context, StateMachine controller, String iface) {
+ super(TAG, controller.getHandler());
+
+ mContext = context;
+ mController = controller;
+ mIfaceName = iface;
+
+ addState(mStoppedState);
+ addState(mDhcpState);
+ addState(mDhcpInitState, mDhcpState);
+ addState(mWaitBeforeStartState, mDhcpState);
+ addState(mDhcpSelectingState, mDhcpState);
+ addState(mDhcpRequestingState, mDhcpState);
+ addState(mDhcpHaveLeaseState, mDhcpState);
+ addState(mConfiguringInterfaceState, mDhcpHaveLeaseState);
+ addState(mDhcpBoundState, mDhcpHaveLeaseState);
+ addState(mWaitBeforeRenewalState, mDhcpHaveLeaseState);
+ addState(mDhcpRenewingState, mDhcpHaveLeaseState);
+ addState(mDhcpRebindingState, mDhcpHaveLeaseState);
+ addState(mDhcpInitRebootState, mDhcpState);
+ addState(mDhcpRebootingState, mDhcpState);
+
+ setInitialState(mStoppedState);
+
+ mRandom = new Random();
+
+ // Used to schedule packet retransmissions.
+ mKickAlarm = makeWakeupMessage("KICK", CMD_KICK);
+ // Used to time out PacketRetransmittingStates.
+ mTimeoutAlarm = makeWakeupMessage("TIMEOUT", CMD_TIMEOUT);
+ // Used to schedule DHCP reacquisition.
+ mRenewAlarm = makeWakeupMessage("RENEW", CMD_RENEW_DHCP);
+ mRebindAlarm = makeWakeupMessage("REBIND", CMD_REBIND_DHCP);
+ mExpiryAlarm = makeWakeupMessage("EXPIRY", CMD_EXPIRE_DHCP);
+ }
+
+ public void registerForPreDhcpNotification() {
+ mRegisteredForPreDhcpNotification = true;
+ }
+
+ public static DhcpClient makeDhcpClient(
+ Context context, StateMachine controller, InterfaceParams ifParams) {
+ DhcpClient client = new DhcpClient(context, controller, ifParams.name);
+ client.mIface = ifParams;
+ client.start();
+ return client;
+ }
+
+ private boolean initInterface() {
+ if (mIface == null) mIface = InterfaceParams.getByName(mIfaceName);
+ if (mIface == null) {
+ Log.e(TAG, "Can't determine InterfaceParams for " + mIfaceName);
+ return false;
+ }
+
+ mHwAddr = mIface.macAddr.toByteArray();
+ mInterfaceBroadcastAddr = makePacketSocketAddress(mIface.index, DhcpPacket.ETHER_BROADCAST);
+ return true;
+ }
+
+ private void startNewTransaction() {
+ mTransactionId = mRandom.nextInt();
+ mTransactionStartMillis = SystemClock.elapsedRealtime();
+ }
+
+ private boolean initSockets() {
+ return initPacketSocket() && initUdpSocket();
+ }
+
+ private boolean initPacketSocket() {
+ try {
+ mPacketSock = Os.socket(AF_PACKET, SOCK_RAW, ETH_P_IP);
+ SocketAddress addr = makePacketSocketAddress((short) ETH_P_IP, mIface.index);
+ Os.bind(mPacketSock, addr);
+ NetworkUtils.attachDhcpFilter(mPacketSock);
+ } catch(SocketException|ErrnoException e) {
+ Log.e(TAG, "Error creating packet socket", e);
+ return false;
+ }
+ return true;
+ }
+
+ private boolean initUdpSocket() {
+ final int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_DHCP);
+ try {
+ mUdpSock = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ SocketUtils.bindSocketToInterface(mUdpSock, mIfaceName);
+ Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_REUSEADDR, 1);
+ Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_BROADCAST, 1);
+ Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_RCVBUF, 0);
+ Os.bind(mUdpSock, Inet4Address.ANY, DhcpPacket.DHCP_CLIENT);
+ } catch(SocketException|ErrnoException e) {
+ Log.e(TAG, "Error creating UDP socket", e);
+ return false;
+ } finally {
+ TrafficStats.setThreadStatsTag(oldTag);
+ }
+ return true;
+ }
+
+ private boolean connectUdpSock(Inet4Address to) {
+ try {
+ Os.connect(mUdpSock, to, DhcpPacket.DHCP_SERVER);
+ return true;
+ } catch (SocketException|ErrnoException e) {
+ Log.e(TAG, "Error connecting UDP socket", e);
+ return false;
+ }
+ }
+
+ private static void closeQuietly(FileDescriptor fd) {
+ try {
+ IoBridge.closeAndSignalBlockedThreads(fd);
+ } catch (IOException ignored) {}
+ }
+
+ private void closeSockets() {
+ closeQuietly(mUdpSock);
+ closeQuietly(mPacketSock);
+ }
+
+ class ReceiveThread extends Thread {
+
+ private final byte[] mPacket = new byte[DhcpPacket.MAX_LENGTH];
+ private volatile boolean mStopped = false;
+
+ public void halt() {
+ mStopped = true;
+ closeSockets(); // Interrupts the read() call the thread is blocked in.
+ }
+
+ @Override
+ public void run() {
+ if (DBG) Log.d(TAG, "Receive thread started");
+ while (!mStopped) {
+ int length = 0; // Or compiler can't tell it's initialized if a parse error occurs.
+ try {
+ length = Os.read(mPacketSock, mPacket, 0, mPacket.length);
+ DhcpPacket packet = null;
+ packet = DhcpPacket.decodeFullPacket(mPacket, length, DhcpPacket.ENCAP_L2);
+ if (DBG) Log.d(TAG, "Received packet: " + packet);
+ sendMessage(CMD_RECEIVED_PACKET, packet);
+ } catch (IOException|ErrnoException e) {
+ if (!mStopped) {
+ Log.e(TAG, "Read error", e);
+ logError(DhcpErrorEvent.RECEIVE_ERROR);
+ }
+ } catch (DhcpPacket.ParseException e) {
+ Log.e(TAG, "Can't parse packet: " + e.getMessage());
+ if (PACKET_DBG) {
+ Log.d(TAG, HexDump.dumpHexString(mPacket, 0, length));
+ }
+ if (e.errorCode == DhcpErrorEvent.DHCP_NO_COOKIE) {
+ int snetTagId = 0x534e4554;
+ String bugId = "31850211";
+ int uid = -1;
+ String data = DhcpPacket.ParseException.class.getName();
+ EventLog.writeEvent(snetTagId, bugId, uid, data);
+ }
+ logError(e.errorCode);
+ }
+ }
+ if (DBG) Log.d(TAG, "Receive thread stopped");
+ }
+ }
+
+ private short getSecs() {
+ return (short) ((SystemClock.elapsedRealtime() - mTransactionStartMillis) / 1000);
+ }
+
+ private boolean transmitPacket(ByteBuffer buf, String description, int encap, Inet4Address to) {
+ try {
+ if (encap == DhcpPacket.ENCAP_L2) {
+ if (DBG) Log.d(TAG, "Broadcasting " + description);
+ Os.sendto(mPacketSock, buf.array(), 0, buf.limit(), 0, mInterfaceBroadcastAddr);
+ } else if (encap == DhcpPacket.ENCAP_BOOTP && to.equals(INADDR_BROADCAST)) {
+ if (DBG) Log.d(TAG, "Broadcasting " + description);
+ // We only send L3-encapped broadcasts in DhcpRebindingState,
+ // where we have an IP address and an unconnected UDP socket.
+ //
+ // N.B.: We only need this codepath because DhcpRequestPacket
+ // hardcodes the source IP address to 0.0.0.0. We could reuse
+ // the packet socket if this ever changes.
+ Os.sendto(mUdpSock, buf, 0, to, DhcpPacket.DHCP_SERVER);
+ } else {
+ // It's safe to call getpeername here, because we only send unicast packets if we
+ // have an IP address, and we connect the UDP socket in DhcpBoundState#enter.
+ if (DBG) Log.d(TAG, String.format("Unicasting %s to %s",
+ description, Os.getpeername(mUdpSock)));
+ Os.write(mUdpSock, buf);
+ }
+ } catch(ErrnoException|IOException e) {
+ Log.e(TAG, "Can't send packet: ", e);
+ return false;
+ }
+ return true;
+ }
+
+ private boolean sendDiscoverPacket() {
+ ByteBuffer packet = DhcpPacket.buildDiscoverPacket(
+ DhcpPacket.ENCAP_L2, mTransactionId, getSecs(), mHwAddr,
+ DO_UNICAST, REQUESTED_PARAMS);
+ return transmitPacket(packet, "DHCPDISCOVER", DhcpPacket.ENCAP_L2, INADDR_BROADCAST);
+ }
+
+ private boolean sendRequestPacket(
+ Inet4Address clientAddress, Inet4Address requestedAddress,
+ Inet4Address serverAddress, Inet4Address to) {
+ // TODO: should we use the transaction ID from the server?
+ final int encap = INADDR_ANY.equals(clientAddress)
+ ? DhcpPacket.ENCAP_L2 : DhcpPacket.ENCAP_BOOTP;
+
+ ByteBuffer packet = DhcpPacket.buildRequestPacket(
+ encap, mTransactionId, getSecs(), clientAddress,
+ DO_UNICAST, mHwAddr, requestedAddress,
+ serverAddress, REQUESTED_PARAMS, null);
+ String serverStr = (serverAddress != null) ? serverAddress.getHostAddress() : null;
+ String description = "DHCPREQUEST ciaddr=" + clientAddress.getHostAddress() +
+ " request=" + requestedAddress.getHostAddress() +
+ " serverid=" + serverStr;
+ return transmitPacket(packet, description, encap, to);
+ }
+
+ private void scheduleLeaseTimers() {
+ if (mDhcpLeaseExpiry == 0) {
+ Log.d(TAG, "Infinite lease, no timer scheduling needed");
+ return;
+ }
+
+ final long now = SystemClock.elapsedRealtime();
+
+ // TODO: consider getting the renew and rebind timers from T1 and T2.
+ // See also:
+ // https://tools.ietf.org/html/rfc2131#section-4.4.5
+ // https://tools.ietf.org/html/rfc1533#section-9.9
+ // https://tools.ietf.org/html/rfc1533#section-9.10
+ final long remainingDelay = mDhcpLeaseExpiry - now;
+ final long renewDelay = remainingDelay / 2;
+ final long rebindDelay = remainingDelay * 7 / 8;
+ mRenewAlarm.schedule(now + renewDelay);
+ mRebindAlarm.schedule(now + rebindDelay);
+ mExpiryAlarm.schedule(now + remainingDelay);
+ Log.d(TAG, "Scheduling renewal in " + (renewDelay / 1000) + "s");
+ Log.d(TAG, "Scheduling rebind in " + (rebindDelay / 1000) + "s");
+ Log.d(TAG, "Scheduling expiry in " + (remainingDelay / 1000) + "s");
+ }
+
+ private void notifySuccess() {
+ mController.sendMessage(
+ CMD_POST_DHCP_ACTION, DHCP_SUCCESS, 0, new DhcpResults(mDhcpLease));
+ }
+
+ private void notifyFailure() {
+ mController.sendMessage(CMD_POST_DHCP_ACTION, DHCP_FAILURE, 0, null);
+ }
+
+ private void acceptDhcpResults(DhcpResults results, String msg) {
+ mDhcpLease = results;
+ mOffer = null;
+ Log.d(TAG, msg + " lease: " + mDhcpLease);
+ notifySuccess();
+ }
+
+ private void clearDhcpState() {
+ mDhcpLease = null;
+ mDhcpLeaseExpiry = 0;
+ mOffer = null;
+ }
+
+ /**
+ * Quit the DhcpStateMachine.
+ *
+ * @hide
+ */
+ public void doQuit() {
+ Log.d(TAG, "doQuit");
+ quit();
+ }
+
+ @Override
+ protected void onQuitting() {
+ Log.d(TAG, "onQuitting");
+ mController.sendMessage(CMD_ON_QUIT);
+ }
+
+ abstract class LoggingState extends State {
+ private long mEnterTimeMs;
+
+ @Override
+ public void enter() {
+ if (STATE_DBG) Log.d(TAG, "Entering state " + getName());
+ mEnterTimeMs = SystemClock.elapsedRealtime();
+ }
+
+ @Override
+ public void exit() {
+ long durationMs = SystemClock.elapsedRealtime() - mEnterTimeMs;
+ logState(getName(), (int) durationMs);
+ }
+
+ private String messageName(int what) {
+ return sMessageNames.get(what, Integer.toString(what));
+ }
+
+ private String messageToString(Message message) {
+ long now = SystemClock.uptimeMillis();
+ return new StringBuilder(" ")
+ .append(message.getWhen() - now)
+ .append(messageName(message.what))
+ .append(" ").append(message.arg1)
+ .append(" ").append(message.arg2)
+ .append(" ").append(message.obj)
+ .toString();
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ if (MSG_DBG) {
+ Log.d(TAG, getName() + messageToString(message));
+ }
+ return NOT_HANDLED;
+ }
+
+ @Override
+ public String getName() {
+ // All DhcpClient's states are inner classes with a well defined name.
+ // Use getSimpleName() and avoid super's getName() creating new String instances.
+ return getClass().getSimpleName();
+ }
+ }
+
+ // Sends CMD_PRE_DHCP_ACTION to the controller, waits for the controller to respond with
+ // CMD_PRE_DHCP_ACTION_COMPLETE, and then transitions to mOtherState.
+ abstract class WaitBeforeOtherState extends LoggingState {
+ protected State mOtherState;
+
+ @Override
+ public void enter() {
+ super.enter();
+ mController.sendMessage(CMD_PRE_DHCP_ACTION);
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ super.processMessage(message);
+ switch (message.what) {
+ case CMD_PRE_DHCP_ACTION_COMPLETE:
+ transitionTo(mOtherState);
+ return HANDLED;
+ default:
+ return NOT_HANDLED;
+ }
+ }
+ }
+
+ class StoppedState extends State {
+ @Override
+ public boolean processMessage(Message message) {
+ switch (message.what) {
+ case CMD_START_DHCP:
+ if (mRegisteredForPreDhcpNotification) {
+ transitionTo(mWaitBeforeStartState);
+ } else {
+ transitionTo(mDhcpInitState);
+ }
+ return HANDLED;
+ default:
+ return NOT_HANDLED;
+ }
+ }
+ }
+
+ class WaitBeforeStartState extends WaitBeforeOtherState {
+ public WaitBeforeStartState(State otherState) {
+ super();
+ mOtherState = otherState;
+ }
+ }
+
+ class WaitBeforeRenewalState extends WaitBeforeOtherState {
+ public WaitBeforeRenewalState(State otherState) {
+ super();
+ mOtherState = otherState;
+ }
+ }
+
+ class DhcpState extends State {
+ @Override
+ public void enter() {
+ clearDhcpState();
+ if (initInterface() && initSockets()) {
+ mReceiveThread = new ReceiveThread();
+ mReceiveThread.start();
+ } else {
+ notifyFailure();
+ transitionTo(mStoppedState);
+ }
+ }
+
+ @Override
+ public void exit() {
+ if (mReceiveThread != null) {
+ mReceiveThread.halt(); // Also closes sockets.
+ mReceiveThread = null;
+ }
+ clearDhcpState();
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ super.processMessage(message);
+ switch (message.what) {
+ case CMD_STOP_DHCP:
+ transitionTo(mStoppedState);
+ return HANDLED;
+ default:
+ return NOT_HANDLED;
+ }
+ }
+ }
+
+ public boolean isValidPacket(DhcpPacket packet) {
+ // TODO: check checksum.
+ int xid = packet.getTransactionId();
+ if (xid != mTransactionId) {
+ Log.d(TAG, "Unexpected transaction ID " + xid + ", expected " + mTransactionId);
+ return false;
+ }
+ if (!Arrays.equals(packet.getClientMac(), mHwAddr)) {
+ Log.d(TAG, "MAC addr mismatch: got " +
+ HexDump.toHexString(packet.getClientMac()) + ", expected " +
+ HexDump.toHexString(packet.getClientMac()));
+ return false;
+ }
+ return true;
+ }
+
+ public void setDhcpLeaseExpiry(DhcpPacket packet) {
+ long leaseTimeMillis = packet.getLeaseTimeMillis();
+ mDhcpLeaseExpiry =
+ (leaseTimeMillis > 0) ? SystemClock.elapsedRealtime() + leaseTimeMillis : 0;
+ }
+
+ /**
+ * Retransmits packets using jittered exponential backoff with an optional timeout. Packet
+ * transmission is triggered by CMD_KICK, which is sent by an AlarmManager alarm. If a subclass
+ * sets mTimeout to a positive value, then timeout() is called by an AlarmManager alarm mTimeout
+ * milliseconds after entering the state. Kicks and timeouts are cancelled when leaving the
+ * state.
+ *
+ * Concrete subclasses must implement sendPacket, which is called when the alarm fires and a
+ * packet needs to be transmitted, and receivePacket, which is triggered by CMD_RECEIVED_PACKET
+ * sent by the receive thread. They may also set mTimeout and implement timeout.
+ */
+ abstract class PacketRetransmittingState extends LoggingState {
+
+ private int mTimer;
+ protected int mTimeout = 0;
+
+ @Override
+ public void enter() {
+ super.enter();
+ initTimer();
+ maybeInitTimeout();
+ sendMessage(CMD_KICK);
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ super.processMessage(message);
+ switch (message.what) {
+ case CMD_KICK:
+ sendPacket();
+ scheduleKick();
+ return HANDLED;
+ case CMD_RECEIVED_PACKET:
+ receivePacket((DhcpPacket) message.obj);
+ return HANDLED;
+ case CMD_TIMEOUT:
+ timeout();
+ return HANDLED;
+ default:
+ return NOT_HANDLED;
+ }
+ }
+
+ @Override
+ public void exit() {
+ super.exit();
+ mKickAlarm.cancel();
+ mTimeoutAlarm.cancel();
+ }
+
+ abstract protected boolean sendPacket();
+ abstract protected void receivePacket(DhcpPacket packet);
+ protected void timeout() {}
+
+ protected void initTimer() {
+ mTimer = FIRST_TIMEOUT_MS;
+ }
+
+ protected int jitterTimer(int baseTimer) {
+ int maxJitter = baseTimer / 10;
+ int jitter = mRandom.nextInt(2 * maxJitter) - maxJitter;
+ return baseTimer + jitter;
+ }
+
+ protected void scheduleKick() {
+ long now = SystemClock.elapsedRealtime();
+ long timeout = jitterTimer(mTimer);
+ long alarmTime = now + timeout;
+ mKickAlarm.schedule(alarmTime);
+ mTimer *= 2;
+ if (mTimer > MAX_TIMEOUT_MS) {
+ mTimer = MAX_TIMEOUT_MS;
+ }
+ }
+
+ protected void maybeInitTimeout() {
+ if (mTimeout > 0) {
+ long alarmTime = SystemClock.elapsedRealtime() + mTimeout;
+ mTimeoutAlarm.schedule(alarmTime);
+ }
+ }
+ }
+
+ class DhcpInitState extends PacketRetransmittingState {
+ public DhcpInitState() {
+ super();
+ }
+
+ @Override
+ public void enter() {
+ super.enter();
+ startNewTransaction();
+ mLastInitEnterTime = SystemClock.elapsedRealtime();
+ }
+
+ protected boolean sendPacket() {
+ return sendDiscoverPacket();
+ }
+
+ protected void receivePacket(DhcpPacket packet) {
+ if (!isValidPacket(packet)) return;
+ if (!(packet instanceof DhcpOfferPacket)) return;
+ mOffer = packet.toDhcpResults();
+ if (mOffer != null) {
+ Log.d(TAG, "Got pending lease: " + mOffer);
+ transitionTo(mDhcpRequestingState);
+ }
+ }
+ }
+
+ // Not implemented. We request the first offer we receive.
+ class DhcpSelectingState extends LoggingState {
+ }
+
+ class DhcpRequestingState extends PacketRetransmittingState {
+ public DhcpRequestingState() {
+ mTimeout = DHCP_TIMEOUT_MS / 2;
+ }
+
+ protected boolean sendPacket() {
+ return sendRequestPacket(
+ INADDR_ANY, // ciaddr
+ (Inet4Address) mOffer.ipAddress.getAddress(), // DHCP_REQUESTED_IP
+ (Inet4Address) mOffer.serverAddress, // DHCP_SERVER_IDENTIFIER
+ INADDR_BROADCAST); // packet destination address
+ }
+
+ protected void receivePacket(DhcpPacket packet) {
+ if (!isValidPacket(packet)) return;
+ if ((packet instanceof DhcpAckPacket)) {
+ DhcpResults results = packet.toDhcpResults();
+ if (results != null) {
+ setDhcpLeaseExpiry(packet);
+ acceptDhcpResults(results, "Confirmed");
+ transitionTo(mConfiguringInterfaceState);
+ }
+ } else if (packet instanceof DhcpNakPacket) {
+ // TODO: Wait a while before returning into INIT state.
+ Log.d(TAG, "Received NAK, returning to INIT");
+ mOffer = null;
+ transitionTo(mDhcpInitState);
+ }
+ }
+
+ @Override
+ protected void timeout() {
+ // After sending REQUESTs unsuccessfully for a while, go back to init.
+ transitionTo(mDhcpInitState);
+ }
+ }
+
+ class DhcpHaveLeaseState extends State {
+ @Override
+ public boolean processMessage(Message message) {
+ switch (message.what) {
+ case CMD_EXPIRE_DHCP:
+ Log.d(TAG, "Lease expired!");
+ notifyFailure();
+ transitionTo(mDhcpInitState);
+ return HANDLED;
+ default:
+ return NOT_HANDLED;
+ }
+ }
+
+ @Override
+ public void exit() {
+ // Clear any extant alarms.
+ mRenewAlarm.cancel();
+ mRebindAlarm.cancel();
+ mExpiryAlarm.cancel();
+ clearDhcpState();
+ // Tell IpManager to clear the IPv4 address. There is no need to
+ // wait for confirmation since any subsequent packets are sent from
+ // INADDR_ANY anyway (DISCOVER, REQUEST).
+ mController.sendMessage(CMD_CLEAR_LINKADDRESS);
+ }
+ }
+
+ class ConfiguringInterfaceState extends LoggingState {
+ @Override
+ public void enter() {
+ super.enter();
+ mController.sendMessage(CMD_CONFIGURE_LINKADDRESS, mDhcpLease.ipAddress);
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ super.processMessage(message);
+ switch (message.what) {
+ case EVENT_LINKADDRESS_CONFIGURED:
+ transitionTo(mDhcpBoundState);
+ return HANDLED;
+ default:
+ return NOT_HANDLED;
+ }
+ }
+ }
+
+ class DhcpBoundState extends LoggingState {
+ @Override
+ public void enter() {
+ super.enter();
+ if (mDhcpLease.serverAddress != null && !connectUdpSock(mDhcpLease.serverAddress)) {
+ // There's likely no point in going into DhcpInitState here, we'll probably
+ // just repeat the transaction, get the same IP address as before, and fail.
+ //
+ // NOTE: It is observed that connectUdpSock() basically never fails, due to
+ // SO_BINDTODEVICE. Examining the local socket address shows it will happily
+ // return an IPv4 address from another interface, or even return "0.0.0.0".
+ //
+ // TODO: Consider deleting this check, following testing on several kernels.
+ notifyFailure();
+ transitionTo(mStoppedState);
+ }
+
+ scheduleLeaseTimers();
+ logTimeToBoundState();
+ }
+
+ @Override
+ public void exit() {
+ super.exit();
+ mLastBoundExitTime = SystemClock.elapsedRealtime();
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ super.processMessage(message);
+ switch (message.what) {
+ case CMD_RENEW_DHCP:
+ if (mRegisteredForPreDhcpNotification) {
+ transitionTo(mWaitBeforeRenewalState);
+ } else {
+ transitionTo(mDhcpRenewingState);
+ }
+ return HANDLED;
+ default:
+ return NOT_HANDLED;
+ }
+ }
+
+ private void logTimeToBoundState() {
+ long now = SystemClock.elapsedRealtime();
+ if (mLastBoundExitTime > mLastInitEnterTime) {
+ logState(DhcpClientEvent.RENEWING_BOUND, (int)(now - mLastBoundExitTime));
+ } else {
+ logState(DhcpClientEvent.INITIAL_BOUND, (int)(now - mLastInitEnterTime));
+ }
+ }
+ }
+
+ abstract class DhcpReacquiringState extends PacketRetransmittingState {
+ protected String mLeaseMsg;
+
+ @Override
+ public void enter() {
+ super.enter();
+ startNewTransaction();
+ }
+
+ abstract protected Inet4Address packetDestination();
+
+ protected boolean sendPacket() {
+ return sendRequestPacket(
+ (Inet4Address) mDhcpLease.ipAddress.getAddress(), // ciaddr
+ INADDR_ANY, // DHCP_REQUESTED_IP
+ null, // DHCP_SERVER_IDENTIFIER
+ packetDestination()); // packet destination address
+ }
+
+ protected void receivePacket(DhcpPacket packet) {
+ if (!isValidPacket(packet)) return;
+ if ((packet instanceof DhcpAckPacket)) {
+ final DhcpResults results = packet.toDhcpResults();
+ if (results != null) {
+ if (!mDhcpLease.ipAddress.equals(results.ipAddress)) {
+ Log.d(TAG, "Renewed lease not for our current IP address!");
+ notifyFailure();
+ transitionTo(mDhcpInitState);
+ }
+ setDhcpLeaseExpiry(packet);
+ // Updating our notion of DhcpResults here only causes the
+ // DNS servers and routes to be updated in LinkProperties
+ // in IpManager and by any overridden relevant handlers of
+ // the registered IpManager.Callback. IP address changes
+ // are not supported here.
+ acceptDhcpResults(results, mLeaseMsg);
+ transitionTo(mDhcpBoundState);
+ }
+ } else if (packet instanceof DhcpNakPacket) {
+ Log.d(TAG, "Received NAK, returning to INIT");
+ notifyFailure();
+ transitionTo(mDhcpInitState);
+ }
+ }
+ }
+
+ class DhcpRenewingState extends DhcpReacquiringState {
+ public DhcpRenewingState() {
+ mLeaseMsg = "Renewed";
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ if (super.processMessage(message) == HANDLED) {
+ return HANDLED;
+ }
+
+ switch (message.what) {
+ case CMD_REBIND_DHCP:
+ transitionTo(mDhcpRebindingState);
+ return HANDLED;
+ default:
+ return NOT_HANDLED;
+ }
+ }
+
+ @Override
+ protected Inet4Address packetDestination() {
+ // Not specifying a SERVER_IDENTIFIER option is a violation of RFC 2131, but...
+ // http://b/25343517 . Try to make things work anyway by using broadcast renews.
+ return (mDhcpLease.serverAddress != null) ?
+ mDhcpLease.serverAddress : INADDR_BROADCAST;
+ }
+ }
+
+ class DhcpRebindingState extends DhcpReacquiringState {
+ public DhcpRebindingState() {
+ mLeaseMsg = "Rebound";
+ }
+
+ @Override
+ public void enter() {
+ super.enter();
+
+ // We need to broadcast and possibly reconnect the socket to a
+ // completely different server.
+ closeQuietly(mUdpSock);
+ if (!initUdpSocket()) {
+ Log.e(TAG, "Failed to recreate UDP socket");
+ transitionTo(mDhcpInitState);
+ }
+ }
+
+ @Override
+ protected Inet4Address packetDestination() {
+ return INADDR_BROADCAST;
+ }
+ }
+
+ class DhcpInitRebootState extends LoggingState {
+ }
+
+ class DhcpRebootingState extends LoggingState {
+ }
+
+ private void logError(int errorCode) {
+ mMetricsLog.log(mIfaceName, new DhcpErrorEvent(errorCode));
+ }
+
+ private void logState(String name, int durationMs) {
+ final DhcpClientEvent event = new DhcpClientEvent.Builder()
+ .setMsg(name)
+ .setDurationMs(durationMs)
+ .build();
+ mMetricsLog.log(mIfaceName, event);
+ }
+}
diff --git a/services/net/java/android/net/dhcp/DhcpDeclinePacket.java b/packages/NetworkStack/src/android/net/dhcp/DhcpDeclinePacket.java
similarity index 100%
rename from services/net/java/android/net/dhcp/DhcpDeclinePacket.java
rename to packages/NetworkStack/src/android/net/dhcp/DhcpDeclinePacket.java
diff --git a/services/net/java/android/net/dhcp/DhcpDiscoverPacket.java b/packages/NetworkStack/src/android/net/dhcp/DhcpDiscoverPacket.java
similarity index 100%
rename from services/net/java/android/net/dhcp/DhcpDiscoverPacket.java
rename to packages/NetworkStack/src/android/net/dhcp/DhcpDiscoverPacket.java
diff --git a/services/net/java/android/net/dhcp/DhcpInformPacket.java b/packages/NetworkStack/src/android/net/dhcp/DhcpInformPacket.java
similarity index 100%
rename from services/net/java/android/net/dhcp/DhcpInformPacket.java
rename to packages/NetworkStack/src/android/net/dhcp/DhcpInformPacket.java
diff --git a/services/net/java/android/net/dhcp/DhcpNakPacket.java b/packages/NetworkStack/src/android/net/dhcp/DhcpNakPacket.java
similarity index 100%
rename from services/net/java/android/net/dhcp/DhcpNakPacket.java
rename to packages/NetworkStack/src/android/net/dhcp/DhcpNakPacket.java
diff --git a/services/net/java/android/net/dhcp/DhcpOfferPacket.java b/packages/NetworkStack/src/android/net/dhcp/DhcpOfferPacket.java
similarity index 100%
rename from services/net/java/android/net/dhcp/DhcpOfferPacket.java
rename to packages/NetworkStack/src/android/net/dhcp/DhcpOfferPacket.java
diff --git a/services/net/java/android/net/dhcp/DhcpPacket.java b/packages/NetworkStack/src/android/net/dhcp/DhcpPacket.java
similarity index 100%
rename from services/net/java/android/net/dhcp/DhcpPacket.java
rename to packages/NetworkStack/src/android/net/dhcp/DhcpPacket.java
diff --git a/services/net/java/android/net/dhcp/DhcpReleasePacket.java b/packages/NetworkStack/src/android/net/dhcp/DhcpReleasePacket.java
similarity index 100%
rename from services/net/java/android/net/dhcp/DhcpReleasePacket.java
rename to packages/NetworkStack/src/android/net/dhcp/DhcpReleasePacket.java
diff --git a/services/net/java/android/net/dhcp/DhcpRequestPacket.java b/packages/NetworkStack/src/android/net/dhcp/DhcpRequestPacket.java
similarity index 100%
rename from services/net/java/android/net/dhcp/DhcpRequestPacket.java
rename to packages/NetworkStack/src/android/net/dhcp/DhcpRequestPacket.java
diff --git a/services/net/java/android/net/ip/ConnectivityPacketTracker.java b/packages/NetworkStack/src/android/net/ip/ConnectivityPacketTracker.java
similarity index 100%
rename from services/net/java/android/net/ip/ConnectivityPacketTracker.java
rename to packages/NetworkStack/src/android/net/ip/ConnectivityPacketTracker.java
diff --git a/packages/NetworkStack/src/android/net/ip/IpClient.java b/packages/NetworkStack/src/android/net/ip/IpClient.java
new file mode 100644
index 0000000..ad7f85d
--- /dev/null
+++ b/packages/NetworkStack/src/android/net/ip/IpClient.java
@@ -0,0 +1,1691 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.ip;
+
+import static android.net.shared.IpConfigurationParcelableUtil.toStableParcelable;
+import static android.net.shared.LinkPropertiesParcelableUtil.fromStableParcelable;
+import static android.net.shared.LinkPropertiesParcelableUtil.toStableParcelable;
+
+import static com.android.server.util.PermissionUtil.checkNetworkStackCallingPermission;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.DhcpResults;
+import android.net.INetd;
+import android.net.IpPrefix;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.ProvisioningConfigurationParcelable;
+import android.net.ProxyInfo;
+import android.net.ProxyInfoParcelable;
+import android.net.RouteInfo;
+import android.net.apf.ApfCapabilities;
+import android.net.apf.ApfFilter;
+import android.net.dhcp.DhcpClient;
+import android.net.ip.IIpClientCallbacks;
+import android.net.metrics.IpConnectivityLog;
+import android.net.metrics.IpManagerEvent;
+import android.net.shared.InitialConfiguration;
+import android.net.shared.NetdService;
+import android.net.shared.ProvisioningConfiguration;
+import android.net.util.InterfaceParams;
+import android.net.util.SharedLog;
+import android.os.ConditionVariable;
+import android.os.INetworkManagementService;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.text.TextUtils;
+import android.util.LocalLog;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.IState;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.MessageUtils;
+import com.android.internal.util.Preconditions;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
+import com.android.internal.util.WakeupMessage;
+import com.android.server.net.NetlinkTracker;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.net.InetAddress;
+import java.util.Collection;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CountDownLatch;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+
+/**
+ * IpClient
+ *
+ * This class provides the interface to IP-layer provisioning and maintenance
+ * functionality that can be used by transport layers like Wi-Fi, Ethernet,
+ * et cetera.
+ *
+ * [ Lifetime ]
+ * IpClient is designed to be instantiated as soon as the interface name is
+ * known and can be as long-lived as the class containing it (i.e. declaring
+ * it "private final" is okay).
+ *
+ * @hide
+ */
+public class IpClient extends StateMachine {
+ private static final boolean DBG = false;
+
+ // For message logging.
+ private static final Class[] sMessageClasses = { IpClient.class, DhcpClient.class };
+ private static final SparseArray<String> sWhatToString =
+ MessageUtils.findMessageNames(sMessageClasses);
+ // Two static concurrent hashmaps of interface name to logging classes.
+ // One holds StateMachine logs and the other connectivity packet logs.
+ private static final ConcurrentHashMap<String, SharedLog> sSmLogs = new ConcurrentHashMap<>();
+ private static final ConcurrentHashMap<String, LocalLog> sPktLogs = new ConcurrentHashMap<>();
+
+ /**
+ * Dump all state machine and connectivity packet logs to the specified writer.
+ * @param skippedIfaces Interfaces for which logs should not be dumped.
+ */
+ public static void dumpAllLogs(PrintWriter writer, Set<String> skippedIfaces) {
+ for (String ifname : sSmLogs.keySet()) {
+ if (skippedIfaces.contains(ifname)) continue;
+
+ writer.println(String.format("--- BEGIN %s ---", ifname));
+
+ final SharedLog smLog = sSmLogs.get(ifname);
+ if (smLog != null) {
+ writer.println("State machine log:");
+ smLog.dump(null, writer, null);
+ }
+
+ writer.println("");
+
+ final LocalLog pktLog = sPktLogs.get(ifname);
+ if (pktLog != null) {
+ writer.println("Connectivity packet log:");
+ pktLog.readOnlyLocalLog().dump(null, writer, null);
+ }
+
+ writer.println(String.format("--- END %s ---", ifname));
+ }
+ }
+
+ // Use a wrapper class to log in order to ensure complete and detailed
+ // logging. This method is lighter weight than annotations/reflection
+ // and has the following benefits:
+ //
+ // - No invoked method can be forgotten.
+ // Any new method added to IpClient.Callback must be overridden
+ // here or it will never be called.
+ //
+ // - No invoking call site can be forgotten.
+ // Centralized logging in this way means call sites don't need to
+ // remember to log, and therefore no call site can be forgotten.
+ //
+ // - No variation in log format among call sites.
+ // Encourages logging of any available arguments, and all call sites
+ // are necessarily logged identically.
+ //
+ // NOTE: Log first because passed objects may or may not be thread-safe and
+ // once passed on to the callback they may be modified by another thread.
+ //
+ // TODO: Find an lighter weight approach.
+ public static class IpClientCallbacksWrapper {
+ private static final String PREFIX = "INVOKE ";
+ private final IIpClientCallbacks mCallback;
+ private final SharedLog mLog;
+
+ @VisibleForTesting
+ protected IpClientCallbacksWrapper(IIpClientCallbacks callback, SharedLog log) {
+ mCallback = callback;
+ mLog = log;
+ }
+
+ private void log(String msg) {
+ mLog.log(PREFIX + msg);
+ }
+
+ private void log(String msg, Throwable e) {
+ mLog.e(PREFIX + msg, e);
+ }
+
+ public void onPreDhcpAction() {
+ log("onPreDhcpAction()");
+ try {
+ mCallback.onPreDhcpAction();
+ } catch (RemoteException e) {
+ log("Failed to call onPreDhcpAction", e);
+ }
+ }
+
+ public void onPostDhcpAction() {
+ log("onPostDhcpAction()");
+ try {
+ mCallback.onPostDhcpAction();
+ } catch (RemoteException e) {
+ log("Failed to call onPostDhcpAction", e);
+ }
+ }
+
+ public void onNewDhcpResults(DhcpResults dhcpResults) {
+ log("onNewDhcpResults({" + dhcpResults + "})");
+ try {
+ mCallback.onNewDhcpResults(toStableParcelable(dhcpResults));
+ } catch (RemoteException e) {
+ log("Failed to call onNewDhcpResults", e);
+ }
+ }
+
+ public void onProvisioningSuccess(LinkProperties newLp) {
+ log("onProvisioningSuccess({" + newLp + "})");
+ try {
+ mCallback.onProvisioningSuccess(toStableParcelable(newLp));
+ } catch (RemoteException e) {
+ log("Failed to call onProvisioningSuccess", e);
+ }
+ }
+
+ public void onProvisioningFailure(LinkProperties newLp) {
+ log("onProvisioningFailure({" + newLp + "})");
+ try {
+ mCallback.onProvisioningFailure(toStableParcelable(newLp));
+ } catch (RemoteException e) {
+ log("Failed to call onProvisioningFailure", e);
+ }
+ }
+
+ public void onLinkPropertiesChange(LinkProperties newLp) {
+ log("onLinkPropertiesChange({" + newLp + "})");
+ try {
+ mCallback.onLinkPropertiesChange(toStableParcelable(newLp));
+ } catch (RemoteException e) {
+ log("Failed to call onLinkPropertiesChange", e);
+ }
+ }
+
+ public void onReachabilityLost(String logMsg) {
+ log("onReachabilityLost(" + logMsg + ")");
+ try {
+ mCallback.onReachabilityLost(logMsg);
+ } catch (RemoteException e) {
+ log("Failed to call onReachabilityLost", e);
+ }
+ }
+
+ public void onQuit() {
+ log("onQuit()");
+ try {
+ mCallback.onQuit();
+ } catch (RemoteException e) {
+ log("Failed to call onQuit", e);
+ }
+ }
+
+ public void installPacketFilter(byte[] filter) {
+ log("installPacketFilter(byte[" + filter.length + "])");
+ try {
+ mCallback.installPacketFilter(filter);
+ } catch (RemoteException e) {
+ log("Failed to call installPacketFilter", e);
+ }
+ }
+
+ public void startReadPacketFilter() {
+ log("startReadPacketFilter()");
+ try {
+ mCallback.startReadPacketFilter();
+ } catch (RemoteException e) {
+ log("Failed to call startReadPacketFilter", e);
+ }
+ }
+
+ public void setFallbackMulticastFilter(boolean enabled) {
+ log("setFallbackMulticastFilter(" + enabled + ")");
+ try {
+ mCallback.setFallbackMulticastFilter(enabled);
+ } catch (RemoteException e) {
+ log("Failed to call setFallbackMulticastFilter", e);
+ }
+ }
+
+ public void setNeighborDiscoveryOffload(boolean enable) {
+ log("setNeighborDiscoveryOffload(" + enable + ")");
+ try {
+ mCallback.setNeighborDiscoveryOffload(enable);
+ } catch (RemoteException e) {
+ log("Failed to call setNeighborDiscoveryOffload", e);
+ }
+ }
+ }
+
+ public static final String DUMP_ARG_CONFIRM = "confirm";
+
+ private static final int CMD_TERMINATE_AFTER_STOP = 1;
+ private static final int CMD_STOP = 2;
+ private static final int CMD_START = 3;
+ private static final int CMD_CONFIRM = 4;
+ private static final int EVENT_PRE_DHCP_ACTION_COMPLETE = 5;
+ // Triggered by NetlinkTracker to communicate netlink events.
+ private static final int EVENT_NETLINK_LINKPROPERTIES_CHANGED = 6;
+ private static final int CMD_UPDATE_TCP_BUFFER_SIZES = 7;
+ private static final int CMD_UPDATE_HTTP_PROXY = 8;
+ private static final int CMD_SET_MULTICAST_FILTER = 9;
+ private static final int EVENT_PROVISIONING_TIMEOUT = 10;
+ private static final int EVENT_DHCPACTION_TIMEOUT = 11;
+ private static final int EVENT_READ_PACKET_FILTER_COMPLETE = 12;
+
+ // Internal commands to use instead of trying to call transitionTo() inside
+ // a given State's enter() method. Calling transitionTo() from enter/exit
+ // encounters a Log.wtf() that can cause trouble on eng builds.
+ private static final int CMD_JUMP_STARTED_TO_RUNNING = 100;
+ private static final int CMD_JUMP_RUNNING_TO_STOPPING = 101;
+ private static final int CMD_JUMP_STOPPING_TO_STOPPED = 102;
+
+ // IpClient shares a handler with DhcpClient: commands must not overlap
+ public static final int DHCPCLIENT_CMD_BASE = 1000;
+
+ private static final int MAX_LOG_RECORDS = 500;
+ private static final int MAX_PACKET_RECORDS = 100;
+
+ private static final boolean NO_CALLBACKS = false;
+ private static final boolean SEND_CALLBACKS = true;
+
+ // This must match the interface prefix in clatd.c.
+ // TODO: Revert this hack once IpClient and Nat464Xlat work in concert.
+ private static final String CLAT_PREFIX = "v4-";
+
+ private static final int IMMEDIATE_FAILURE_DURATION = 0;
+
+ private static final int PROV_CHANGE_STILL_NOT_PROVISIONED = 1;
+ private static final int PROV_CHANGE_LOST_PROVISIONING = 2;
+ private static final int PROV_CHANGE_GAINED_PROVISIONING = 3;
+ private static final int PROV_CHANGE_STILL_PROVISIONED = 4;
+
+ private final State mStoppedState = new StoppedState();
+ private final State mStoppingState = new StoppingState();
+ private final State mStartedState = new StartedState();
+ private final State mRunningState = new RunningState();
+
+ private final String mTag;
+ private final Context mContext;
+ private final String mInterfaceName;
+ private final String mClatInterfaceName;
+ @VisibleForTesting
+ protected final IpClientCallbacksWrapper mCallback;
+ private final Dependencies mDependencies;
+ private final CountDownLatch mShutdownLatch;
+ private final ConnectivityManager mCm;
+ private final INetworkManagementService mNwService;
+ private final NetlinkTracker mNetlinkTracker;
+ private final WakeupMessage mProvisioningTimeoutAlarm;
+ private final WakeupMessage mDhcpActionTimeoutAlarm;
+ private final SharedLog mLog;
+ private final LocalLog mConnectivityPacketLog;
+ private final MessageHandlingLogger mMsgStateLogger;
+ private final IpConnectivityLog mMetricsLog = new IpConnectivityLog();
+ private final InterfaceController mInterfaceCtrl;
+
+ private InterfaceParams mInterfaceParams;
+
+ /**
+ * Non-final member variables accessed only from within our StateMachine.
+ */
+ private LinkProperties mLinkProperties;
+ private android.net.shared.ProvisioningConfiguration mConfiguration;
+ private IpReachabilityMonitor mIpReachabilityMonitor;
+ private DhcpClient mDhcpClient;
+ private DhcpResults mDhcpResults;
+ private String mTcpBufferSizes;
+ private ProxyInfo mHttpProxy;
+ private ApfFilter mApfFilter;
+ private boolean mMulticastFiltering;
+ private long mStartTimeMillis;
+
+ /**
+ * Reading the snapshot is an asynchronous operation initiated by invoking
+ * Callback.startReadPacketFilter() and completed when the WiFi Service responds with an
+ * EVENT_READ_PACKET_FILTER_COMPLETE message. The mApfDataSnapshotComplete condition variable
+ * signals when a new snapshot is ready.
+ */
+ private final ConditionVariable mApfDataSnapshotComplete = new ConditionVariable();
+
+ public static class Dependencies {
+ public INetworkManagementService getNMS() {
+ return INetworkManagementService.Stub.asInterface(
+ ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
+ }
+
+ public INetd getNetd() {
+ return NetdService.getInstance();
+ }
+
+ /**
+ * Get interface parameters for the specified interface.
+ */
+ public InterfaceParams getInterfaceParams(String ifname) {
+ return InterfaceParams.getByName(ifname);
+ }
+ }
+
+ public IpClient(Context context, String ifName, IIpClientCallbacks callback) {
+ this(context, ifName, callback, new Dependencies());
+ }
+
+ /**
+ * An expanded constructor, useful for dependency injection.
+ * TODO: migrate all test users to mock IpClient directly and remove this ctor.
+ */
+ public IpClient(Context context, String ifName, IIpClientCallbacks callback,
+ INetworkManagementService nwService) {
+ this(context, ifName, callback, new Dependencies() {
+ @Override
+ public INetworkManagementService getNMS() {
+ return nwService;
+ }
+ });
+ }
+
+ @VisibleForTesting
+ IpClient(Context context, String ifName, IIpClientCallbacks callback, Dependencies deps) {
+ super(IpClient.class.getSimpleName() + "." + ifName);
+ Preconditions.checkNotNull(ifName);
+ Preconditions.checkNotNull(callback);
+
+ mTag = getName();
+
+ mContext = context;
+ mInterfaceName = ifName;
+ mClatInterfaceName = CLAT_PREFIX + ifName;
+ mDependencies = deps;
+ mShutdownLatch = new CountDownLatch(1);
+ mCm = mContext.getSystemService(ConnectivityManager.class);
+ mNwService = deps.getNMS();
+
+ sSmLogs.putIfAbsent(mInterfaceName, new SharedLog(MAX_LOG_RECORDS, mTag));
+ mLog = sSmLogs.get(mInterfaceName);
+ sPktLogs.putIfAbsent(mInterfaceName, new LocalLog(MAX_PACKET_RECORDS));
+ mConnectivityPacketLog = sPktLogs.get(mInterfaceName);
+ mMsgStateLogger = new MessageHandlingLogger();
+ mCallback = new IpClientCallbacksWrapper(callback, mLog);
+
+ // TODO: Consider creating, constructing, and passing in some kind of
+ // InterfaceController.Dependencies class.
+ mInterfaceCtrl = new InterfaceController(mInterfaceName, deps.getNetd(), mLog);
+
+ mNetlinkTracker = new NetlinkTracker(
+ mInterfaceName,
+ new NetlinkTracker.Callback() {
+ @Override
+ public void update() {
+ sendMessage(EVENT_NETLINK_LINKPROPERTIES_CHANGED);
+ }
+ }) {
+ @Override
+ public void interfaceAdded(String iface) {
+ super.interfaceAdded(iface);
+ if (mClatInterfaceName.equals(iface)) {
+ mCallback.setNeighborDiscoveryOffload(false);
+ } else if (!mInterfaceName.equals(iface)) {
+ return;
+ }
+
+ final String msg = "interfaceAdded(" + iface + ")";
+ logMsg(msg);
+ }
+
+ @Override
+ public void interfaceRemoved(String iface) {
+ super.interfaceRemoved(iface);
+ // TODO: Also observe mInterfaceName going down and take some
+ // kind of appropriate action.
+ if (mClatInterfaceName.equals(iface)) {
+ // TODO: consider sending a message to the IpClient main
+ // StateMachine thread, in case "NDO enabled" state becomes
+ // tied to more things that 464xlat operation.
+ mCallback.setNeighborDiscoveryOffload(true);
+ } else if (!mInterfaceName.equals(iface)) {
+ return;
+ }
+
+ final String msg = "interfaceRemoved(" + iface + ")";
+ logMsg(msg);
+ }
+
+ private void logMsg(String msg) {
+ Log.d(mTag, msg);
+ getHandler().post(() -> mLog.log("OBSERVED " + msg));
+ }
+ };
+
+ mLinkProperties = new LinkProperties();
+ mLinkProperties.setInterfaceName(mInterfaceName);
+
+ mProvisioningTimeoutAlarm = new WakeupMessage(mContext, getHandler(),
+ mTag + ".EVENT_PROVISIONING_TIMEOUT", EVENT_PROVISIONING_TIMEOUT);
+ mDhcpActionTimeoutAlarm = new WakeupMessage(mContext, getHandler(),
+ mTag + ".EVENT_DHCPACTION_TIMEOUT", EVENT_DHCPACTION_TIMEOUT);
+
+ // Anything the StateMachine may access must have been instantiated
+ // before this point.
+ configureAndStartStateMachine();
+
+ // Anything that may send messages to the StateMachine must only be
+ // configured to do so after the StateMachine has started (above).
+ startStateMachineUpdaters();
+ }
+
+ /**
+ * Make a IIpClient connector to communicate with this IpClient.
+ */
+ public IIpClient makeConnector() {
+ return new IpClientConnector();
+ }
+
+ class IpClientConnector extends IIpClient.Stub {
+ @Override
+ public void completedPreDhcpAction() {
+ checkNetworkStackCallingPermission();
+ IpClient.this.completedPreDhcpAction();
+ }
+ @Override
+ public void confirmConfiguration() {
+ checkNetworkStackCallingPermission();
+ IpClient.this.confirmConfiguration();
+ }
+ @Override
+ public void readPacketFilterComplete(byte[] data) {
+ checkNetworkStackCallingPermission();
+ IpClient.this.readPacketFilterComplete(data);
+ }
+ @Override
+ public void shutdown() {
+ checkNetworkStackCallingPermission();
+ IpClient.this.shutdown();
+ }
+ @Override
+ public void startProvisioning(ProvisioningConfigurationParcelable req) {
+ checkNetworkStackCallingPermission();
+ IpClient.this.startProvisioning(ProvisioningConfiguration.fromStableParcelable(req));
+ }
+ @Override
+ public void stop() {
+ checkNetworkStackCallingPermission();
+ IpClient.this.stop();
+ }
+ @Override
+ public void setTcpBufferSizes(String tcpBufferSizes) {
+ checkNetworkStackCallingPermission();
+ IpClient.this.setTcpBufferSizes(tcpBufferSizes);
+ }
+ @Override
+ public void setHttpProxy(ProxyInfoParcelable proxyInfo) {
+ checkNetworkStackCallingPermission();
+ IpClient.this.setHttpProxy(fromStableParcelable(proxyInfo));
+ }
+ @Override
+ public void setMulticastFilter(boolean enabled) {
+ checkNetworkStackCallingPermission();
+ IpClient.this.setMulticastFilter(enabled);
+ }
+ }
+
+ public String getInterfaceName() {
+ return mInterfaceName;
+ }
+
+ private void configureAndStartStateMachine() {
+ // CHECKSTYLE:OFF IndentationCheck
+ addState(mStoppedState);
+ addState(mStartedState);
+ addState(mRunningState, mStartedState);
+ addState(mStoppingState);
+ // CHECKSTYLE:ON IndentationCheck
+
+ setInitialState(mStoppedState);
+
+ super.start();
+ }
+
+ private void startStateMachineUpdaters() {
+ try {
+ mNwService.registerObserver(mNetlinkTracker);
+ } catch (RemoteException e) {
+ logError("Couldn't register NetlinkTracker: %s", e);
+ }
+ }
+
+ private void stopStateMachineUpdaters() {
+ try {
+ mNwService.unregisterObserver(mNetlinkTracker);
+ } catch (RemoteException e) {
+ logError("Couldn't unregister NetlinkTracker: %s", e);
+ }
+ }
+
+ @Override
+ protected void onQuitting() {
+ mCallback.onQuit();
+ mShutdownLatch.countDown();
+ }
+
+ /**
+ * Shut down this IpClient instance altogether.
+ */
+ public void shutdown() {
+ stop();
+ sendMessage(CMD_TERMINATE_AFTER_STOP);
+ }
+
+ /**
+ * Start provisioning with the provided parameters.
+ */
+ public void startProvisioning(ProvisioningConfiguration req) {
+ if (!req.isValid()) {
+ doImmediateProvisioningFailure(IpManagerEvent.ERROR_INVALID_PROVISIONING);
+ return;
+ }
+
+ mInterfaceParams = mDependencies.getInterfaceParams(mInterfaceName);
+ if (mInterfaceParams == null) {
+ logError("Failed to find InterfaceParams for " + mInterfaceName);
+ doImmediateProvisioningFailure(IpManagerEvent.ERROR_INTERFACE_NOT_FOUND);
+ return;
+ }
+
+ mCallback.setNeighborDiscoveryOffload(true);
+ sendMessage(CMD_START, new android.net.shared.ProvisioningConfiguration(req));
+ }
+
+ /**
+ * Stop this IpClient.
+ *
+ * <p>This does not shut down the StateMachine itself, which is handled by {@link #shutdown()}.
+ */
+ public void stop() {
+ sendMessage(CMD_STOP);
+ }
+
+ /**
+ * Confirm the provisioning configuration.
+ */
+ public void confirmConfiguration() {
+ sendMessage(CMD_CONFIRM);
+ }
+
+ /**
+ * For clients using {@link ProvisioningConfiguration.Builder#withPreDhcpAction()}, must be
+ * called after {@link IIpClientCallbacks#onPreDhcpAction} to indicate that DHCP is clear to
+ * proceed.
+ */
+ public void completedPreDhcpAction() {
+ sendMessage(EVENT_PRE_DHCP_ACTION_COMPLETE);
+ }
+
+ /**
+ * Indicate that packet filter read is complete.
+ */
+ public void readPacketFilterComplete(byte[] data) {
+ sendMessage(EVENT_READ_PACKET_FILTER_COMPLETE, data);
+ }
+
+ /**
+ * Set the TCP buffer sizes to use.
+ *
+ * This may be called, repeatedly, at any time before or after a call to
+ * #startProvisioning(). The setting is cleared upon calling #stop().
+ */
+ public void setTcpBufferSizes(String tcpBufferSizes) {
+ sendMessage(CMD_UPDATE_TCP_BUFFER_SIZES, tcpBufferSizes);
+ }
+
+ /**
+ * Set the HTTP Proxy configuration to use.
+ *
+ * This may be called, repeatedly, at any time before or after a call to
+ * #startProvisioning(). The setting is cleared upon calling #stop().
+ */
+ public void setHttpProxy(ProxyInfo proxyInfo) {
+ sendMessage(CMD_UPDATE_HTTP_PROXY, proxyInfo);
+ }
+
+ /**
+ * Enable or disable the multicast filter. Attempts to use APF to accomplish the filtering,
+ * if not, Callback.setFallbackMulticastFilter() is called.
+ */
+ public void setMulticastFilter(boolean enabled) {
+ sendMessage(CMD_SET_MULTICAST_FILTER, enabled);
+ }
+
+ /**
+ * Dump logs of this IpClient.
+ */
+ public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+ if (args != null && args.length > 0 && DUMP_ARG_CONFIRM.equals(args[0])) {
+ // Execute confirmConfiguration() and take no further action.
+ confirmConfiguration();
+ return;
+ }
+
+ // Thread-unsafe access to mApfFilter but just used for debugging.
+ final ApfFilter apfFilter = mApfFilter;
+ final android.net.shared.ProvisioningConfiguration provisioningConfig = mConfiguration;
+ final ApfCapabilities apfCapabilities = (provisioningConfig != null)
+ ? provisioningConfig.mApfCapabilities : null;
+
+ IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
+ pw.println(mTag + " APF dump:");
+ pw.increaseIndent();
+ if (apfFilter != null) {
+ if (apfCapabilities.hasDataAccess()) {
+ // Request a new snapshot, then wait for it.
+ mApfDataSnapshotComplete.close();
+ mCallback.startReadPacketFilter();
+ if (!mApfDataSnapshotComplete.block(1000)) {
+ pw.print("TIMEOUT: DUMPING STALE APF SNAPSHOT");
+ }
+ }
+ apfFilter.dump(pw);
+
+ } else {
+ pw.print("No active ApfFilter; ");
+ if (provisioningConfig == null) {
+ pw.println("IpClient not yet started.");
+ } else if (apfCapabilities == null || apfCapabilities.apfVersionSupported == 0) {
+ pw.println("Hardware does not support APF.");
+ } else {
+ pw.println("ApfFilter not yet started, APF capabilities: " + apfCapabilities);
+ }
+ }
+ pw.decreaseIndent();
+ pw.println();
+ pw.println(mTag + " current ProvisioningConfiguration:");
+ pw.increaseIndent();
+ pw.println(Objects.toString(provisioningConfig, "N/A"));
+ pw.decreaseIndent();
+
+ final IpReachabilityMonitor iprm = mIpReachabilityMonitor;
+ if (iprm != null) {
+ pw.println();
+ pw.println(mTag + " current IpReachabilityMonitor state:");
+ pw.increaseIndent();
+ iprm.dump(pw);
+ pw.decreaseIndent();
+ }
+
+ pw.println();
+ pw.println(mTag + " StateMachine dump:");
+ pw.increaseIndent();
+ mLog.dump(fd, pw, args);
+ pw.decreaseIndent();
+
+ pw.println();
+ pw.println(mTag + " connectivity packet log:");
+ pw.println();
+ pw.println("Debug with python and scapy via:");
+ pw.println("shell$ python");
+ pw.println(">>> from scapy import all as scapy");
+ pw.println(">>> scapy.Ether(\"<paste_hex_string>\".decode(\"hex\")).show2()");
+ pw.println();
+
+ pw.increaseIndent();
+ mConnectivityPacketLog.readOnlyLocalLog().dump(fd, pw, args);
+ pw.decreaseIndent();
+ }
+
+
+ /**
+ * Internals.
+ */
+
+ @Override
+ protected String getWhatToString(int what) {
+ return sWhatToString.get(what, "UNKNOWN: " + Integer.toString(what));
+ }
+
+ @Override
+ protected String getLogRecString(Message msg) {
+ final String logLine = String.format(
+ "%s/%d %d %d %s [%s]",
+ mInterfaceName, (mInterfaceParams == null) ? -1 : mInterfaceParams.index,
+ msg.arg1, msg.arg2, Objects.toString(msg.obj), mMsgStateLogger);
+
+ final String richerLogLine = getWhatToString(msg.what) + " " + logLine;
+ mLog.log(richerLogLine);
+ if (DBG) {
+ Log.d(mTag, richerLogLine);
+ }
+
+ mMsgStateLogger.reset();
+ return logLine;
+ }
+
+ @Override
+ protected boolean recordLogRec(Message msg) {
+ // Don't log EVENT_NETLINK_LINKPROPERTIES_CHANGED. They can be noisy,
+ // and we already log any LinkProperties change that results in an
+ // invocation of IpClient.Callback#onLinkPropertiesChange().
+ final boolean shouldLog = (msg.what != EVENT_NETLINK_LINKPROPERTIES_CHANGED);
+ if (!shouldLog) {
+ mMsgStateLogger.reset();
+ }
+ return shouldLog;
+ }
+
+ private void logError(String fmt, Object... args) {
+ final String msg = "ERROR " + String.format(fmt, args);
+ Log.e(mTag, msg);
+ mLog.log(msg);
+ }
+
+ // This needs to be called with care to ensure that our LinkProperties
+ // are in sync with the actual LinkProperties of the interface. For example,
+ // we should only call this if we know for sure that there are no IP addresses
+ // assigned to the interface, etc.
+ private void resetLinkProperties() {
+ mNetlinkTracker.clearLinkProperties();
+ mConfiguration = null;
+ mDhcpResults = null;
+ mTcpBufferSizes = "";
+ mHttpProxy = null;
+
+ mLinkProperties = new LinkProperties();
+ mLinkProperties.setInterfaceName(mInterfaceName);
+ }
+
+ private void recordMetric(final int type) {
+ // We may record error metrics prior to starting.
+ // Map this to IMMEDIATE_FAILURE_DURATION.
+ final long duration = (mStartTimeMillis > 0)
+ ? (SystemClock.elapsedRealtime() - mStartTimeMillis)
+ : IMMEDIATE_FAILURE_DURATION;
+ mMetricsLog.log(mInterfaceName, new IpManagerEvent(type, duration));
+ }
+
+ // For now: use WifiStateMachine's historical notion of provisioned.
+ @VisibleForTesting
+ static boolean isProvisioned(LinkProperties lp, InitialConfiguration config) {
+ // For historical reasons, we should connect even if all we have is
+ // an IPv4 address and nothing else.
+ if (lp.hasIPv4Address() || lp.isProvisioned()) {
+ return true;
+ }
+ if (config == null) {
+ return false;
+ }
+
+ // When an InitialConfiguration is specified, ignore any difference with previous
+ // properties and instead check if properties observed match the desired properties.
+ return config.isProvisionedBy(lp.getLinkAddresses(), lp.getRoutes());
+ }
+
+ // TODO: Investigate folding all this into the existing static function
+ // LinkProperties.compareProvisioning() or some other single function that
+ // takes two LinkProperties objects and returns a ProvisioningChange
+ // object that is a correct and complete assessment of what changed, taking
+ // account of the asymmetries described in the comments in this function.
+ // Then switch to using it everywhere (IpReachabilityMonitor, etc.).
+ private int compareProvisioning(LinkProperties oldLp, LinkProperties newLp) {
+ int delta;
+ InitialConfiguration config = mConfiguration != null ? mConfiguration.mInitialConfig : null;
+ final boolean wasProvisioned = isProvisioned(oldLp, config);
+ final boolean isProvisioned = isProvisioned(newLp, config);
+
+ if (!wasProvisioned && isProvisioned) {
+ delta = PROV_CHANGE_GAINED_PROVISIONING;
+ } else if (wasProvisioned && isProvisioned) {
+ delta = PROV_CHANGE_STILL_PROVISIONED;
+ } else if (!wasProvisioned && !isProvisioned) {
+ delta = PROV_CHANGE_STILL_NOT_PROVISIONED;
+ } else {
+ // (wasProvisioned && !isProvisioned)
+ //
+ // Note that this is true even if we lose a configuration element
+ // (e.g., a default gateway) that would not be required to advance
+ // into provisioned state. This is intended: if we have a default
+ // router and we lose it, that's a sure sign of a problem, but if
+ // we connect to a network with no IPv4 DNS servers, we consider
+ // that to be a network without DNS servers and connect anyway.
+ //
+ // See the comment below.
+ delta = PROV_CHANGE_LOST_PROVISIONING;
+ }
+
+ final boolean lostIPv6 = oldLp.isIPv6Provisioned() && !newLp.isIPv6Provisioned();
+ final boolean lostIPv4Address = oldLp.hasIPv4Address() && !newLp.hasIPv4Address();
+ final boolean lostIPv6Router = oldLp.hasIPv6DefaultRoute() && !newLp.hasIPv6DefaultRoute();
+
+ // If bad wifi avoidance is disabled, then ignore IPv6 loss of
+ // provisioning. Otherwise, when a hotspot that loses Internet
+ // access sends out a 0-lifetime RA to its clients, the clients
+ // will disconnect and then reconnect, avoiding the bad hotspot,
+ // instead of getting stuck on the bad hotspot. http://b/31827713 .
+ //
+ // This is incorrect because if the hotspot then regains Internet
+ // access with a different prefix, TCP connections on the
+ // deprecated addresses will remain stuck.
+ //
+ // Note that we can still be disconnected by IpReachabilityMonitor
+ // if the IPv6 default gateway (but not the IPv6 DNS servers; see
+ // accompanying code in IpReachabilityMonitor) is unreachable.
+ final boolean ignoreIPv6ProvisioningLoss =
+ mConfiguration != null && mConfiguration.mUsingMultinetworkPolicyTracker
+ && mCm.getAvoidBadWifi();
+
+ // Additionally:
+ //
+ // Partial configurations (e.g., only an IPv4 address with no DNS
+ // servers and no default route) are accepted as long as DHCPv4
+ // succeeds. On such a network, isProvisioned() will always return
+ // false, because the configuration is not complete, but we want to
+ // connect anyway. It might be a disconnected network such as a
+ // Chromecast or a wireless printer, for example.
+ //
+ // Because on such a network isProvisioned() will always return false,
+ // delta will never be LOST_PROVISIONING. So check for loss of
+ // provisioning here too.
+ if (lostIPv4Address || (lostIPv6 && !ignoreIPv6ProvisioningLoss)) {
+ delta = PROV_CHANGE_LOST_PROVISIONING;
+ }
+
+ // Additionally:
+ //
+ // If the previous link properties had a global IPv6 address and an
+ // IPv6 default route then also consider the loss of that default route
+ // to be a loss of provisioning. See b/27962810.
+ if (oldLp.hasGlobalIPv6Address() && (lostIPv6Router && !ignoreIPv6ProvisioningLoss)) {
+ delta = PROV_CHANGE_LOST_PROVISIONING;
+ }
+
+ return delta;
+ }
+
+ private void dispatchCallback(int delta, LinkProperties newLp) {
+ switch (delta) {
+ case PROV_CHANGE_GAINED_PROVISIONING:
+ if (DBG) {
+ Log.d(mTag, "onProvisioningSuccess()");
+ }
+ recordMetric(IpManagerEvent.PROVISIONING_OK);
+ mCallback.onProvisioningSuccess(newLp);
+ break;
+
+ case PROV_CHANGE_LOST_PROVISIONING:
+ if (DBG) {
+ Log.d(mTag, "onProvisioningFailure()");
+ }
+ recordMetric(IpManagerEvent.PROVISIONING_FAIL);
+ mCallback.onProvisioningFailure(newLp);
+ break;
+
+ default:
+ if (DBG) {
+ Log.d(mTag, "onLinkPropertiesChange()");
+ }
+ mCallback.onLinkPropertiesChange(newLp);
+ break;
+ }
+ }
+
+ // Updates all IpClient-related state concerned with LinkProperties.
+ // Returns a ProvisioningChange for possibly notifying other interested
+ // parties that are not fronted by IpClient.
+ private int setLinkProperties(LinkProperties newLp) {
+ if (mApfFilter != null) {
+ mApfFilter.setLinkProperties(newLp);
+ }
+ if (mIpReachabilityMonitor != null) {
+ mIpReachabilityMonitor.updateLinkProperties(newLp);
+ }
+
+ int delta = compareProvisioning(mLinkProperties, newLp);
+ mLinkProperties = new LinkProperties(newLp);
+
+ if (delta == PROV_CHANGE_GAINED_PROVISIONING) {
+ // TODO: Add a proper ProvisionedState and cancel the alarm in
+ // its enter() method.
+ mProvisioningTimeoutAlarm.cancel();
+ }
+
+ return delta;
+ }
+
+ private LinkProperties assembleLinkProperties() {
+ // [1] Create a new LinkProperties object to populate.
+ LinkProperties newLp = new LinkProperties();
+ newLp.setInterfaceName(mInterfaceName);
+
+ // [2] Pull in data from netlink:
+ // - IPv4 addresses
+ // - IPv6 addresses
+ // - IPv6 routes
+ // - IPv6 DNS servers
+ //
+ // N.B.: this is fundamentally race-prone and should be fixed by
+ // changing NetlinkTracker from a hybrid edge/level model to an
+ // edge-only model, or by giving IpClient its own netlink socket(s)
+ // so as to track all required information directly.
+ LinkProperties netlinkLinkProperties = mNetlinkTracker.getLinkProperties();
+ newLp.setLinkAddresses(netlinkLinkProperties.getLinkAddresses());
+ for (RouteInfo route : netlinkLinkProperties.getRoutes()) {
+ newLp.addRoute(route);
+ }
+ addAllReachableDnsServers(newLp, netlinkLinkProperties.getDnsServers());
+
+ // [3] Add in data from DHCPv4, if available.
+ //
+ // mDhcpResults is never shared with any other owner so we don't have
+ // to worry about concurrent modification.
+ if (mDhcpResults != null) {
+ for (RouteInfo route : mDhcpResults.getRoutes(mInterfaceName)) {
+ newLp.addRoute(route);
+ }
+ addAllReachableDnsServers(newLp, mDhcpResults.dnsServers);
+ newLp.setDomains(mDhcpResults.domains);
+
+ if (mDhcpResults.mtu != 0) {
+ newLp.setMtu(mDhcpResults.mtu);
+ }
+ }
+
+ // [4] Add in TCP buffer sizes and HTTP Proxy config, if available.
+ if (!TextUtils.isEmpty(mTcpBufferSizes)) {
+ newLp.setTcpBufferSizes(mTcpBufferSizes);
+ }
+ if (mHttpProxy != null) {
+ newLp.setHttpProxy(mHttpProxy);
+ }
+
+ // [5] Add data from InitialConfiguration
+ if (mConfiguration != null && mConfiguration.mInitialConfig != null) {
+ InitialConfiguration config = mConfiguration.mInitialConfig;
+ // Add InitialConfiguration routes and dns server addresses once all addresses
+ // specified in the InitialConfiguration have been observed with Netlink.
+ if (config.isProvisionedBy(newLp.getLinkAddresses(), null)) {
+ for (IpPrefix prefix : config.directlyConnectedRoutes) {
+ newLp.addRoute(new RouteInfo(prefix, null, mInterfaceName));
+ }
+ }
+ addAllReachableDnsServers(newLp, config.dnsServers);
+ }
+ final LinkProperties oldLp = mLinkProperties;
+ if (DBG) {
+ Log.d(mTag, String.format("Netlink-seen LPs: %s, new LPs: %s; old LPs: %s",
+ netlinkLinkProperties, newLp, oldLp));
+ }
+
+ // TODO: also learn via netlink routes specified by an InitialConfiguration and specified
+ // from a static IP v4 config instead of manually patching them in in steps [3] and [5].
+ return newLp;
+ }
+
+ private static void addAllReachableDnsServers(
+ LinkProperties lp, Iterable<InetAddress> dnses) {
+ // TODO: Investigate deleting this reachability check. We should be
+ // able to pass everything down to netd and let netd do evaluation
+ // and RFC6724-style sorting.
+ for (InetAddress dns : dnses) {
+ if (!dns.isAnyLocalAddress() && lp.isReachable(dns)) {
+ lp.addDnsServer(dns);
+ }
+ }
+ }
+
+ // Returns false if we have lost provisioning, true otherwise.
+ private boolean handleLinkPropertiesUpdate(boolean sendCallbacks) {
+ final LinkProperties newLp = assembleLinkProperties();
+ if (Objects.equals(newLp, mLinkProperties)) {
+ return true;
+ }
+ final int delta = setLinkProperties(newLp);
+ if (sendCallbacks) {
+ dispatchCallback(delta, newLp);
+ }
+ return (delta != PROV_CHANGE_LOST_PROVISIONING);
+ }
+
+ private void handleIPv4Success(DhcpResults dhcpResults) {
+ mDhcpResults = new DhcpResults(dhcpResults);
+ final LinkProperties newLp = assembleLinkProperties();
+ final int delta = setLinkProperties(newLp);
+
+ if (DBG) {
+ Log.d(mTag, "onNewDhcpResults(" + Objects.toString(dhcpResults) + ")");
+ }
+ mCallback.onNewDhcpResults(dhcpResults);
+ dispatchCallback(delta, newLp);
+ }
+
+ private void handleIPv4Failure() {
+ // TODO: Investigate deleting this clearIPv4Address() call.
+ //
+ // DhcpClient will send us CMD_CLEAR_LINKADDRESS in all circumstances
+ // that could trigger a call to this function. If we missed handling
+ // that message in StartedState for some reason we would still clear
+ // any addresses upon entry to StoppedState.
+ mInterfaceCtrl.clearIPv4Address();
+ mDhcpResults = null;
+ if (DBG) {
+ Log.d(mTag, "onNewDhcpResults(null)");
+ }
+ mCallback.onNewDhcpResults(null);
+
+ handleProvisioningFailure();
+ }
+
+ private void handleProvisioningFailure() {
+ final LinkProperties newLp = assembleLinkProperties();
+ int delta = setLinkProperties(newLp);
+ // If we've gotten here and we're still not provisioned treat that as
+ // a total loss of provisioning.
+ //
+ // Either (a) static IP configuration failed or (b) DHCPv4 failed AND
+ // there was no usable IPv6 obtained before a non-zero provisioning
+ // timeout expired.
+ //
+ // Regardless: GAME OVER.
+ if (delta == PROV_CHANGE_STILL_NOT_PROVISIONED) {
+ delta = PROV_CHANGE_LOST_PROVISIONING;
+ }
+
+ dispatchCallback(delta, newLp);
+ if (delta == PROV_CHANGE_LOST_PROVISIONING) {
+ transitionTo(mStoppingState);
+ }
+ }
+
+ private void doImmediateProvisioningFailure(int failureType) {
+ logError("onProvisioningFailure(): %s", failureType);
+ recordMetric(failureType);
+ mCallback.onProvisioningFailure(new LinkProperties(mLinkProperties));
+ }
+
+ private boolean startIPv4() {
+ // If we have a StaticIpConfiguration attempt to apply it and
+ // handle the result accordingly.
+ if (mConfiguration.mStaticIpConfig != null) {
+ if (mInterfaceCtrl.setIPv4Address(mConfiguration.mStaticIpConfig.ipAddress)) {
+ handleIPv4Success(new DhcpResults(mConfiguration.mStaticIpConfig));
+ } else {
+ return false;
+ }
+ } else {
+ // Start DHCPv4.
+ mDhcpClient = DhcpClient.makeDhcpClient(mContext, IpClient.this, mInterfaceParams);
+ mDhcpClient.registerForPreDhcpNotification();
+ mDhcpClient.sendMessage(DhcpClient.CMD_START_DHCP);
+ }
+
+ return true;
+ }
+
+ private boolean startIPv6() {
+ return mInterfaceCtrl.setIPv6PrivacyExtensions(true)
+ && mInterfaceCtrl.setIPv6AddrGenModeIfSupported(mConfiguration.mIPv6AddrGenMode)
+ && mInterfaceCtrl.enableIPv6();
+ }
+
+ private boolean applyInitialConfig(InitialConfiguration config) {
+ // TODO: also support specifying a static IPv4 configuration in InitialConfiguration.
+ for (LinkAddress addr : findAll(config.ipAddresses, LinkAddress::isIPv6)) {
+ if (!mInterfaceCtrl.addAddress(addr)) return false;
+ }
+
+ return true;
+ }
+
+ private boolean startIpReachabilityMonitor() {
+ try {
+ // TODO: Fetch these parameters from settings, and install a
+ // settings observer to watch for update and re-program these
+ // parameters (Q: is this level of dynamic updatability really
+ // necessary or does reading from settings at startup suffice?).
+ final int numSolicits = 5;
+ final int interSolicitIntervalMs = 750;
+ setNeighborParameters(mDependencies.getNetd(), mInterfaceName,
+ numSolicits, interSolicitIntervalMs);
+ } catch (Exception e) {
+ mLog.e("Failed to adjust neighbor parameters", e);
+ // Carry on using the system defaults (currently: 3, 1000);
+ }
+
+ try {
+ mIpReachabilityMonitor = new IpReachabilityMonitor(
+ mContext,
+ mInterfaceParams,
+ getHandler(),
+ mLog,
+ new IpReachabilityMonitor.Callback() {
+ @Override
+ public void notifyLost(InetAddress ip, String logMsg) {
+ mCallback.onReachabilityLost(logMsg);
+ }
+ },
+ mConfiguration.mUsingMultinetworkPolicyTracker);
+ } catch (IllegalArgumentException iae) {
+ // Failed to start IpReachabilityMonitor. Log it and call
+ // onProvisioningFailure() immediately.
+ //
+ // See http://b/31038971.
+ logError("IpReachabilityMonitor failure: %s", iae);
+ mIpReachabilityMonitor = null;
+ }
+
+ return (mIpReachabilityMonitor != null);
+ }
+
+ private void stopAllIP() {
+ // We don't need to worry about routes, just addresses, because:
+ // - disableIpv6() will clear autoconf IPv6 routes as well, and
+ // - we don't get IPv4 routes from netlink
+ // so we neither react to nor need to wait for changes in either.
+
+ mInterfaceCtrl.disableIPv6();
+ mInterfaceCtrl.clearAllAddresses();
+ }
+
+ class StoppedState extends State {
+ @Override
+ public void enter() {
+ stopAllIP();
+
+ resetLinkProperties();
+ if (mStartTimeMillis > 0) {
+ // Completed a life-cycle; send a final empty LinkProperties
+ // (cleared in resetLinkProperties() above) and record an event.
+ mCallback.onLinkPropertiesChange(new LinkProperties(mLinkProperties));
+ recordMetric(IpManagerEvent.COMPLETE_LIFECYCLE);
+ mStartTimeMillis = 0;
+ }
+ }
+
+ @Override
+ public boolean processMessage(Message msg) {
+ switch (msg.what) {
+ case CMD_TERMINATE_AFTER_STOP:
+ stopStateMachineUpdaters();
+ quit();
+ break;
+
+ case CMD_STOP:
+ break;
+
+ case CMD_START:
+ mConfiguration = (android.net.shared.ProvisioningConfiguration) msg.obj;
+ transitionTo(mStartedState);
+ break;
+
+ case EVENT_NETLINK_LINKPROPERTIES_CHANGED:
+ handleLinkPropertiesUpdate(NO_CALLBACKS);
+ break;
+
+ case CMD_UPDATE_TCP_BUFFER_SIZES:
+ mTcpBufferSizes = (String) msg.obj;
+ handleLinkPropertiesUpdate(NO_CALLBACKS);
+ break;
+
+ case CMD_UPDATE_HTTP_PROXY:
+ mHttpProxy = (ProxyInfo) msg.obj;
+ handleLinkPropertiesUpdate(NO_CALLBACKS);
+ break;
+
+ case CMD_SET_MULTICAST_FILTER:
+ mMulticastFiltering = (boolean) msg.obj;
+ break;
+
+ case DhcpClient.CMD_ON_QUIT:
+ // Everything is already stopped.
+ logError("Unexpected CMD_ON_QUIT (already stopped).");
+ break;
+
+ default:
+ return NOT_HANDLED;
+ }
+
+ mMsgStateLogger.handled(this, getCurrentState());
+ return HANDLED;
+ }
+ }
+
+ class StoppingState extends State {
+ @Override
+ public void enter() {
+ if (mDhcpClient == null) {
+ // There's no DHCPv4 for which to wait; proceed to stopped.
+ deferMessage(obtainMessage(CMD_JUMP_STOPPING_TO_STOPPED));
+ }
+ }
+
+ @Override
+ public boolean processMessage(Message msg) {
+ switch (msg.what) {
+ case CMD_JUMP_STOPPING_TO_STOPPED:
+ transitionTo(mStoppedState);
+ break;
+
+ case CMD_STOP:
+ break;
+
+ case DhcpClient.CMD_CLEAR_LINKADDRESS:
+ mInterfaceCtrl.clearIPv4Address();
+ break;
+
+ case DhcpClient.CMD_ON_QUIT:
+ mDhcpClient = null;
+ transitionTo(mStoppedState);
+ break;
+
+ default:
+ deferMessage(msg);
+ }
+
+ mMsgStateLogger.handled(this, getCurrentState());
+ return HANDLED;
+ }
+ }
+
+ class StartedState extends State {
+ @Override
+ public void enter() {
+ mStartTimeMillis = SystemClock.elapsedRealtime();
+
+ if (mConfiguration.mProvisioningTimeoutMs > 0) {
+ final long alarmTime = SystemClock.elapsedRealtime()
+ + mConfiguration.mProvisioningTimeoutMs;
+ mProvisioningTimeoutAlarm.schedule(alarmTime);
+ }
+
+ if (readyToProceed()) {
+ deferMessage(obtainMessage(CMD_JUMP_STARTED_TO_RUNNING));
+ } else {
+ // Clear all IPv4 and IPv6 before proceeding to RunningState.
+ // Clean up any leftover state from an abnormal exit from
+ // tethering or during an IpClient restart.
+ stopAllIP();
+ }
+ }
+
+ @Override
+ public void exit() {
+ mProvisioningTimeoutAlarm.cancel();
+ }
+
+ @Override
+ public boolean processMessage(Message msg) {
+ switch (msg.what) {
+ case CMD_JUMP_STARTED_TO_RUNNING:
+ transitionTo(mRunningState);
+ break;
+
+ case CMD_STOP:
+ transitionTo(mStoppingState);
+ break;
+
+ case EVENT_NETLINK_LINKPROPERTIES_CHANGED:
+ handleLinkPropertiesUpdate(NO_CALLBACKS);
+ if (readyToProceed()) {
+ transitionTo(mRunningState);
+ }
+ break;
+
+ case EVENT_PROVISIONING_TIMEOUT:
+ handleProvisioningFailure();
+ break;
+
+ default:
+ // It's safe to process messages out of order because the
+ // only message that can both
+ // a) be received at this time and
+ // b) affect provisioning state
+ // is EVENT_NETLINK_LINKPROPERTIES_CHANGED (handled above).
+ deferMessage(msg);
+ }
+
+ mMsgStateLogger.handled(this, getCurrentState());
+ return HANDLED;
+ }
+
+ private boolean readyToProceed() {
+ return (!mLinkProperties.hasIPv4Address() && !mLinkProperties.hasGlobalIPv6Address());
+ }
+ }
+
+ class RunningState extends State {
+ private ConnectivityPacketTracker mPacketTracker;
+ private boolean mDhcpActionInFlight;
+
+ @Override
+ public void enter() {
+ ApfFilter.ApfConfiguration apfConfig = new ApfFilter.ApfConfiguration();
+ apfConfig.apfCapabilities = mConfiguration.mApfCapabilities;
+ apfConfig.multicastFilter = mMulticastFiltering;
+ // Get the Configuration for ApfFilter from Context
+ apfConfig.ieee802_3Filter =
+ mContext.getResources().getBoolean(R.bool.config_apfDrop802_3Frames);
+ apfConfig.ethTypeBlackList =
+ mContext.getResources().getIntArray(R.array.config_apfEthTypeBlackList);
+ mApfFilter = ApfFilter.maybeCreate(mContext, apfConfig, mInterfaceParams, mCallback);
+ // TODO: investigate the effects of any multicast filtering racing/interfering with the
+ // rest of this IP configuration startup.
+ if (mApfFilter == null) {
+ mCallback.setFallbackMulticastFilter(mMulticastFiltering);
+ }
+
+ mPacketTracker = createPacketTracker();
+ if (mPacketTracker != null) mPacketTracker.start(mConfiguration.mDisplayName);
+
+ if (mConfiguration.mEnableIPv6 && !startIPv6()) {
+ doImmediateProvisioningFailure(IpManagerEvent.ERROR_STARTING_IPV6);
+ enqueueJumpToStoppingState();
+ return;
+ }
+
+ if (mConfiguration.mEnableIPv4 && !startIPv4()) {
+ doImmediateProvisioningFailure(IpManagerEvent.ERROR_STARTING_IPV4);
+ enqueueJumpToStoppingState();
+ return;
+ }
+
+ final InitialConfiguration config = mConfiguration.mInitialConfig;
+ if ((config != null) && !applyInitialConfig(config)) {
+ // TODO introduce a new IpManagerEvent constant to distinguish this error case.
+ doImmediateProvisioningFailure(IpManagerEvent.ERROR_INVALID_PROVISIONING);
+ enqueueJumpToStoppingState();
+ return;
+ }
+
+ if (mConfiguration.mUsingIpReachabilityMonitor && !startIpReachabilityMonitor()) {
+ doImmediateProvisioningFailure(
+ IpManagerEvent.ERROR_STARTING_IPREACHABILITYMONITOR);
+ enqueueJumpToStoppingState();
+ return;
+ }
+ }
+
+ @Override
+ public void exit() {
+ stopDhcpAction();
+
+ if (mIpReachabilityMonitor != null) {
+ mIpReachabilityMonitor.stop();
+ mIpReachabilityMonitor = null;
+ }
+
+ if (mDhcpClient != null) {
+ mDhcpClient.sendMessage(DhcpClient.CMD_STOP_DHCP);
+ mDhcpClient.doQuit();
+ }
+
+ if (mPacketTracker != null) {
+ mPacketTracker.stop();
+ mPacketTracker = null;
+ }
+
+ if (mApfFilter != null) {
+ mApfFilter.shutdown();
+ mApfFilter = null;
+ }
+
+ resetLinkProperties();
+ }
+
+ private void enqueueJumpToStoppingState() {
+ deferMessage(obtainMessage(CMD_JUMP_RUNNING_TO_STOPPING));
+ }
+
+ private ConnectivityPacketTracker createPacketTracker() {
+ try {
+ return new ConnectivityPacketTracker(
+ getHandler(), mInterfaceParams, mConnectivityPacketLog);
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+ }
+
+ private void ensureDhcpAction() {
+ if (!mDhcpActionInFlight) {
+ mCallback.onPreDhcpAction();
+ mDhcpActionInFlight = true;
+ final long alarmTime = SystemClock.elapsedRealtime()
+ + mConfiguration.mRequestedPreDhcpActionMs;
+ mDhcpActionTimeoutAlarm.schedule(alarmTime);
+ }
+ }
+
+ private void stopDhcpAction() {
+ mDhcpActionTimeoutAlarm.cancel();
+ if (mDhcpActionInFlight) {
+ mCallback.onPostDhcpAction();
+ mDhcpActionInFlight = false;
+ }
+ }
+
+ @Override
+ public boolean processMessage(Message msg) {
+ switch (msg.what) {
+ case CMD_JUMP_RUNNING_TO_STOPPING:
+ case CMD_STOP:
+ transitionTo(mStoppingState);
+ break;
+
+ case CMD_START:
+ logError("ALERT: START received in StartedState. Please fix caller.");
+ break;
+
+ case CMD_CONFIRM:
+ // TODO: Possibly introduce a second type of confirmation
+ // that both probes (a) on-link neighbors and (b) does
+ // a DHCPv4 RENEW. We used to do this on Wi-Fi framework
+ // roams.
+ if (mIpReachabilityMonitor != null) {
+ mIpReachabilityMonitor.probeAll();
+ }
+ break;
+
+ case EVENT_PRE_DHCP_ACTION_COMPLETE:
+ // It's possible to reach here if, for example, someone
+ // calls completedPreDhcpAction() after provisioning with
+ // a static IP configuration.
+ if (mDhcpClient != null) {
+ mDhcpClient.sendMessage(DhcpClient.CMD_PRE_DHCP_ACTION_COMPLETE);
+ }
+ break;
+
+ case EVENT_NETLINK_LINKPROPERTIES_CHANGED:
+ if (!handleLinkPropertiesUpdate(SEND_CALLBACKS)) {
+ transitionTo(mStoppingState);
+ }
+ break;
+
+ case CMD_UPDATE_TCP_BUFFER_SIZES:
+ mTcpBufferSizes = (String) msg.obj;
+ // This cannot possibly change provisioning state.
+ handleLinkPropertiesUpdate(SEND_CALLBACKS);
+ break;
+
+ case CMD_UPDATE_HTTP_PROXY:
+ mHttpProxy = (ProxyInfo) msg.obj;
+ // This cannot possibly change provisioning state.
+ handleLinkPropertiesUpdate(SEND_CALLBACKS);
+ break;
+
+ case CMD_SET_MULTICAST_FILTER: {
+ mMulticastFiltering = (boolean) msg.obj;
+ if (mApfFilter != null) {
+ mApfFilter.setMulticastFilter(mMulticastFiltering);
+ } else {
+ mCallback.setFallbackMulticastFilter(mMulticastFiltering);
+ }
+ break;
+ }
+
+ case EVENT_READ_PACKET_FILTER_COMPLETE: {
+ if (mApfFilter != null) {
+ mApfFilter.setDataSnapshot((byte[]) msg.obj);
+ }
+ mApfDataSnapshotComplete.open();
+ break;
+ }
+
+ case EVENT_DHCPACTION_TIMEOUT:
+ stopDhcpAction();
+ break;
+
+ case DhcpClient.CMD_PRE_DHCP_ACTION:
+ if (mConfiguration.mRequestedPreDhcpActionMs > 0) {
+ ensureDhcpAction();
+ } else {
+ sendMessage(EVENT_PRE_DHCP_ACTION_COMPLETE);
+ }
+ break;
+
+ case DhcpClient.CMD_CLEAR_LINKADDRESS:
+ mInterfaceCtrl.clearIPv4Address();
+ break;
+
+ case DhcpClient.CMD_CONFIGURE_LINKADDRESS: {
+ final LinkAddress ipAddress = (LinkAddress) msg.obj;
+ if (mInterfaceCtrl.setIPv4Address(ipAddress)) {
+ mDhcpClient.sendMessage(DhcpClient.EVENT_LINKADDRESS_CONFIGURED);
+ } else {
+ logError("Failed to set IPv4 address.");
+ dispatchCallback(PROV_CHANGE_LOST_PROVISIONING,
+ new LinkProperties(mLinkProperties));
+ transitionTo(mStoppingState);
+ }
+ break;
+ }
+
+ // This message is only received when:
+ //
+ // a) initial address acquisition succeeds,
+ // b) renew succeeds or is NAK'd,
+ // c) rebind succeeds or is NAK'd, or
+ // c) the lease expires,
+ //
+ // but never when initial address acquisition fails. The latter
+ // condition is now governed by the provisioning timeout.
+ case DhcpClient.CMD_POST_DHCP_ACTION:
+ stopDhcpAction();
+
+ switch (msg.arg1) {
+ case DhcpClient.DHCP_SUCCESS:
+ handleIPv4Success((DhcpResults) msg.obj);
+ break;
+ case DhcpClient.DHCP_FAILURE:
+ handleIPv4Failure();
+ break;
+ default:
+ logError("Unknown CMD_POST_DHCP_ACTION status: %s", msg.arg1);
+ }
+ break;
+
+ case DhcpClient.CMD_ON_QUIT:
+ // DHCPv4 quit early for some reason.
+ logError("Unexpected CMD_ON_QUIT.");
+ mDhcpClient = null;
+ break;
+
+ default:
+ return NOT_HANDLED;
+ }
+
+ mMsgStateLogger.handled(this, getCurrentState());
+ return HANDLED;
+ }
+ }
+
+ private static class MessageHandlingLogger {
+ public String processedInState;
+ public String receivedInState;
+
+ public void reset() {
+ processedInState = null;
+ receivedInState = null;
+ }
+
+ public void handled(State processedIn, IState receivedIn) {
+ processedInState = processedIn.getClass().getSimpleName();
+ receivedInState = receivedIn.getName();
+ }
+
+ public String toString() {
+ return String.format("rcvd_in=%s, proc_in=%s",
+ receivedInState, processedInState);
+ }
+ }
+
+ private static void setNeighborParameters(
+ INetd netd, String ifName, int numSolicits, int interSolicitIntervalMs)
+ throws RemoteException, IllegalArgumentException {
+ Preconditions.checkNotNull(netd);
+ Preconditions.checkArgument(!TextUtils.isEmpty(ifName));
+ Preconditions.checkArgument(numSolicits > 0);
+ Preconditions.checkArgument(interSolicitIntervalMs > 0);
+
+ for (int family : new Integer[]{INetd.IPV4, INetd.IPV6}) {
+ netd.setProcSysNet(family, INetd.NEIGH, ifName, "retrans_time_ms",
+ Integer.toString(interSolicitIntervalMs));
+ netd.setProcSysNet(family, INetd.NEIGH, ifName, "ucast_solicit",
+ Integer.toString(numSolicits));
+ }
+ }
+
+ // TODO: extract out into CollectionUtils.
+ static <T> boolean any(Iterable<T> coll, Predicate<T> fn) {
+ for (T t : coll) {
+ if (fn.test(t)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ static <T> boolean all(Iterable<T> coll, Predicate<T> fn) {
+ return !any(coll, not(fn));
+ }
+
+ static <T> Predicate<T> not(Predicate<T> fn) {
+ return (t) -> !fn.test(t);
+ }
+
+ static <T> String join(String delimiter, Collection<T> coll) {
+ return coll.stream().map(Object::toString).collect(Collectors.joining(delimiter));
+ }
+
+ static <T> T find(Iterable<T> coll, Predicate<T> fn) {
+ for (T t: coll) {
+ if (fn.test(t)) {
+ return t;
+ }
+ }
+ return null;
+ }
+
+ static <T> List<T> findAll(Collection<T> coll, Predicate<T> fn) {
+ return coll.stream().filter(fn).collect(Collectors.toList());
+ }
+}
diff --git a/services/net/java/android/net/ip/IpNeighborMonitor.java b/packages/NetworkStack/src/android/net/ip/IpNeighborMonitor.java
similarity index 100%
rename from services/net/java/android/net/ip/IpNeighborMonitor.java
rename to packages/NetworkStack/src/android/net/ip/IpNeighborMonitor.java
diff --git a/services/net/java/android/net/ip/IpReachabilityMonitor.java b/packages/NetworkStack/src/android/net/ip/IpReachabilityMonitor.java
similarity index 100%
rename from services/net/java/android/net/ip/IpReachabilityMonitor.java
rename to packages/NetworkStack/src/android/net/ip/IpReachabilityMonitor.java
diff --git a/services/net/java/android/net/util/ConnectivityPacketSummary.java b/packages/NetworkStack/src/android/net/util/ConnectivityPacketSummary.java
similarity index 79%
rename from services/net/java/android/net/util/ConnectivityPacketSummary.java
rename to packages/NetworkStack/src/android/net/util/ConnectivityPacketSummary.java
index ec833b0..08c3f60 100644
--- a/services/net/java/android/net/util/ConnectivityPacketSummary.java
+++ b/packages/NetworkStack/src/android/net/util/ConnectivityPacketSummary.java
@@ -16,47 +16,46 @@
package android.net.util;
-import static android.net.util.NetworkConstants.ARP_HWTYPE_ETHER;
-import static android.net.util.NetworkConstants.ARP_PAYLOAD_LEN;
-import static android.net.util.NetworkConstants.ARP_REPLY;
-import static android.net.util.NetworkConstants.ARP_REQUEST;
-import static android.net.util.NetworkConstants.DHCP4_CLIENT_PORT;
-import static android.net.util.NetworkConstants.ETHER_ADDR_LEN;
-import static android.net.util.NetworkConstants.ETHER_DST_ADDR_OFFSET;
-import static android.net.util.NetworkConstants.ETHER_HEADER_LEN;
-import static android.net.util.NetworkConstants.ETHER_SRC_ADDR_OFFSET;
-import static android.net.util.NetworkConstants.ETHER_TYPE_ARP;
-import static android.net.util.NetworkConstants.ETHER_TYPE_IPV4;
-import static android.net.util.NetworkConstants.ETHER_TYPE_IPV6;
-import static android.net.util.NetworkConstants.ETHER_TYPE_OFFSET;
-import static android.net.util.NetworkConstants.ICMPV6_HEADER_MIN_LEN;
-import static android.net.util.NetworkConstants.ICMPV6_ND_OPTION_LENGTH_SCALING_FACTOR;
-import static android.net.util.NetworkConstants.ICMPV6_ND_OPTION_MIN_LENGTH;
-import static android.net.util.NetworkConstants.ICMPV6_ND_OPTION_MTU;
-import static android.net.util.NetworkConstants.ICMPV6_ND_OPTION_SLLA;
-import static android.net.util.NetworkConstants.ICMPV6_ND_OPTION_TLLA;
-import static android.net.util.NetworkConstants.ICMPV6_NEIGHBOR_ADVERTISEMENT;
-import static android.net.util.NetworkConstants.ICMPV6_NEIGHBOR_SOLICITATION;
-import static android.net.util.NetworkConstants.ICMPV6_ROUTER_ADVERTISEMENT;
-import static android.net.util.NetworkConstants.ICMPV6_ROUTER_SOLICITATION;
-import static android.net.util.NetworkConstants.IPV4_ADDR_LEN;
-import static android.net.util.NetworkConstants.IPV4_DST_ADDR_OFFSET;
-import static android.net.util.NetworkConstants.IPV4_FLAGS_OFFSET;
-import static android.net.util.NetworkConstants.IPV4_FRAGMENT_MASK;
-import static android.net.util.NetworkConstants.IPV4_HEADER_MIN_LEN;
-import static android.net.util.NetworkConstants.IPV4_IHL_MASK;
-import static android.net.util.NetworkConstants.IPV4_PROTOCOL_OFFSET;
-import static android.net.util.NetworkConstants.IPV4_SRC_ADDR_OFFSET;
-import static android.net.util.NetworkConstants.IPV6_ADDR_LEN;
-import static android.net.util.NetworkConstants.IPV6_HEADER_LEN;
-import static android.net.util.NetworkConstants.IPV6_PROTOCOL_OFFSET;
-import static android.net.util.NetworkConstants.IPV6_SRC_ADDR_OFFSET;
-import static android.net.util.NetworkConstants.UDP_HEADER_LEN;
-import static android.net.util.NetworkConstants.asString;
-import static android.net.util.NetworkConstants.asUint;
import static android.system.OsConstants.IPPROTO_ICMPV6;
import static android.system.OsConstants.IPPROTO_UDP;
+import static com.android.server.util.NetworkStackConstants.ARP_HWTYPE_ETHER;
+import static com.android.server.util.NetworkStackConstants.ARP_PAYLOAD_LEN;
+import static com.android.server.util.NetworkStackConstants.ARP_REPLY;
+import static com.android.server.util.NetworkStackConstants.ARP_REQUEST;
+import static com.android.server.util.NetworkStackConstants.DHCP4_CLIENT_PORT;
+import static com.android.server.util.NetworkStackConstants.ETHER_ADDR_LEN;
+import static com.android.server.util.NetworkStackConstants.ETHER_DST_ADDR_OFFSET;
+import static com.android.server.util.NetworkStackConstants.ETHER_HEADER_LEN;
+import static com.android.server.util.NetworkStackConstants.ETHER_SRC_ADDR_OFFSET;
+import static com.android.server.util.NetworkStackConstants.ETHER_TYPE_ARP;
+import static com.android.server.util.NetworkStackConstants.ETHER_TYPE_IPV4;
+import static com.android.server.util.NetworkStackConstants.ETHER_TYPE_IPV6;
+import static com.android.server.util.NetworkStackConstants.ETHER_TYPE_OFFSET;
+import static com.android.server.util.NetworkStackConstants.ICMPV6_HEADER_MIN_LEN;
+import static com.android.server.util.NetworkStackConstants.ICMPV6_ND_OPTION_LENGTH_SCALING_FACTOR;
+import static com.android.server.util.NetworkStackConstants.ICMPV6_ND_OPTION_MIN_LENGTH;
+import static com.android.server.util.NetworkStackConstants.ICMPV6_ND_OPTION_MTU;
+import static com.android.server.util.NetworkStackConstants.ICMPV6_ND_OPTION_SLLA;
+import static com.android.server.util.NetworkStackConstants.ICMPV6_ND_OPTION_TLLA;
+import static com.android.server.util.NetworkStackConstants.ICMPV6_NEIGHBOR_ADVERTISEMENT;
+import static com.android.server.util.NetworkStackConstants.ICMPV6_NEIGHBOR_SOLICITATION;
+import static com.android.server.util.NetworkStackConstants.ICMPV6_ROUTER_ADVERTISEMENT;
+import static com.android.server.util.NetworkStackConstants.ICMPV6_ROUTER_SOLICITATION;
+import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_LEN;
+import static com.android.server.util.NetworkStackConstants.IPV4_DST_ADDR_OFFSET;
+import static com.android.server.util.NetworkStackConstants.IPV4_FLAGS_OFFSET;
+import static com.android.server.util.NetworkStackConstants.IPV4_FRAGMENT_MASK;
+import static com.android.server.util.NetworkStackConstants.IPV4_HEADER_MIN_LEN;
+import static com.android.server.util.NetworkStackConstants.IPV4_IHL_MASK;
+import static com.android.server.util.NetworkStackConstants.IPV4_PROTOCOL_OFFSET;
+import static com.android.server.util.NetworkStackConstants.IPV4_SRC_ADDR_OFFSET;
+import static com.android.server.util.NetworkStackConstants.IPV6_ADDR_LEN;
+import static com.android.server.util.NetworkStackConstants.IPV6_HEADER_LEN;
+import static com.android.server.util.NetworkStackConstants.IPV6_PROTOCOL_OFFSET;
+import static com.android.server.util.NetworkStackConstants.IPV6_SRC_ADDR_OFFSET;
+import static com.android.server.util.NetworkStackConstants.UDP_HEADER_LEN;
+
import android.net.MacAddress;
import android.net.dhcp.DhcpPacket;
@@ -412,4 +411,25 @@
final String MAC48_FORMAT = "%02x:%02x:%02x:%02x:%02x:%02x";
return String.format(MAC48_FORMAT, printableBytes);
}
+
+ /**
+ * Convenience method to convert an int to a String.
+ */
+ public static String asString(int i) {
+ return Integer.toString(i);
+ }
+
+ /**
+ * Convenience method to read a byte as an unsigned int.
+ */
+ public static int asUint(byte b) {
+ return (b & 0xff);
+ }
+
+ /**
+ * Convenience method to read a short as an unsigned int.
+ */
+ public static int asUint(short s) {
+ return (s & 0xffff);
+ }
}
diff --git a/services/net/java/android/net/util/FdEventsReader.java b/packages/NetworkStack/src/android/net/util/FdEventsReader.java
similarity index 100%
rename from services/net/java/android/net/util/FdEventsReader.java
rename to packages/NetworkStack/src/android/net/util/FdEventsReader.java
diff --git a/services/net/java/android/net/util/PacketReader.java b/packages/NetworkStack/src/android/net/util/PacketReader.java
similarity index 100%
rename from services/net/java/android/net/util/PacketReader.java
rename to packages/NetworkStack/src/android/net/util/PacketReader.java
diff --git a/packages/NetworkStack/src/com/android/server/NetworkStackService.java b/packages/NetworkStack/src/com/android/server/NetworkStackService.java
index cca71e7..4080ddf 100644
--- a/packages/NetworkStack/src/com/android/server/NetworkStackService.java
+++ b/packages/NetworkStack/src/com/android/server/NetworkStackService.java
@@ -39,6 +39,8 @@
import android.net.dhcp.DhcpServingParams;
import android.net.dhcp.DhcpServingParamsParcel;
import android.net.dhcp.IDhcpServerCallbacks;
+import android.net.ip.IIpClientCallbacks;
+import android.net.ip.IpClient;
import android.net.shared.PrivateDnsConfig;
import android.net.util.SharedLog;
import android.os.IBinder;
@@ -50,7 +52,11 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
/**
* Android service used to start the network stack when bound to via an intent.
@@ -80,6 +86,8 @@
private static final int NUM_VALIDATION_LOG_LINES = 20;
private final Context mContext;
private final ConnectivityManager mCm;
+ @GuardedBy("mIpClients")
+ private final ArrayList<WeakReference<IpClient>> mIpClients = new ArrayList<>();
private static final int MAX_VALIDATION_LOGS = 10;
@GuardedBy("mValidationLogs")
@@ -138,6 +146,24 @@
}
@Override
+ public void makeIpClient(String ifName, IIpClientCallbacks cb) throws RemoteException {
+ final IpClient ipClient = new IpClient(mContext, ifName, cb);
+
+ synchronized (mIpClients) {
+ final Iterator<WeakReference<IpClient>> it = mIpClients.iterator();
+ while (it.hasNext()) {
+ final IpClient ipc = it.next().get();
+ if (ipc == null) {
+ it.remove();
+ }
+ }
+ mIpClients.add(new WeakReference<>(ipClient));
+ }
+
+ cb.onIpClientCreated(ipClient.makeConnector());
+ }
+
+ @Override
protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter fout,
@Nullable String[] args) {
checkDumpPermission();
@@ -145,6 +171,33 @@
pw.println("NetworkStack logs:");
mLog.dump(fd, pw, args);
+ // Dump full IpClient logs for non-GCed clients
+ pw.println();
+ pw.println("Recently active IpClient logs:");
+ final ArrayList<IpClient> ipClients = new ArrayList<>();
+ final HashSet<String> dumpedIpClientIfaces = new HashSet<>();
+ synchronized (mIpClients) {
+ for (WeakReference<IpClient> ipcRef : mIpClients) {
+ final IpClient ipc = ipcRef.get();
+ if (ipc != null) {
+ ipClients.add(ipc);
+ }
+ }
+ }
+
+ for (IpClient ipc : ipClients) {
+ pw.println(ipc.getName());
+ pw.increaseIndent();
+ ipc.dump(fd, pw, args);
+ pw.decreaseIndent();
+ dumpedIpClientIfaces.add(ipc.getInterfaceName());
+ }
+
+ // State machine and connectivity metrics logs are kept for GCed IpClients
+ pw.println();
+ pw.println("Other IpClient logs:");
+ IpClient.dumpAllLogs(fout, dumpedIpClientIfaces);
+
pw.println();
pw.println("Validation logs (most recent first):");
synchronized (mValidationLogs) {
diff --git a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
index a3d7852..6b31b82 100644
--- a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
+++ b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
@@ -247,7 +247,6 @@
private final TelephonyManager mTelephonyManager;
private final WifiManager mWifiManager;
private final ConnectivityManager mCm;
- private final NetworkRequest mDefaultRequest;
private final IpConnectivityLog mMetricsLog;
private final Dependencies mDependencies;
@@ -336,7 +335,6 @@
mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
mCm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
- mDefaultRequest = defaultRequest;
// CHECKSTYLE:OFF IndentationCheck
addState(mDefaultState);
@@ -486,8 +484,7 @@
}
private boolean isValidationRequired() {
- return NetworkMonitorUtils.isValidationRequired(
- mDefaultRequest.networkCapabilities, mNetworkCapabilities);
+ return NetworkMonitorUtils.isValidationRequired(mNetworkCapabilities);
}
diff --git a/packages/NetworkStack/src/com/android/server/util/NetworkStackConstants.java b/packages/NetworkStack/src/com/android/server/util/NetworkStackConstants.java
index bb5900c..eedaf30 100644
--- a/packages/NetworkStack/src/com/android/server/util/NetworkStackConstants.java
+++ b/packages/NetworkStack/src/com/android/server/util/NetworkStackConstants.java
@@ -32,12 +32,103 @@
public static final int IPV4_MAX_MTU = 65_535;
/**
+ * Ethernet constants.
+ *
+ * See also:
+ * - https://tools.ietf.org/html/rfc894
+ * - https://tools.ietf.org/html/rfc2464
+ * - https://tools.ietf.org/html/rfc7042
+ * - http://www.iana.org/assignments/ethernet-numbers/ethernet-numbers.xhtml
+ * - http://www.iana.org/assignments/ieee-802-numbers/ieee-802-numbers.xhtml
+ */
+ public static final int ETHER_DST_ADDR_OFFSET = 0;
+ public static final int ETHER_SRC_ADDR_OFFSET = 6;
+ public static final int ETHER_ADDR_LEN = 6;
+ public static final int ETHER_TYPE_OFFSET = 12;
+ public static final int ETHER_TYPE_LENGTH = 2;
+ public static final int ETHER_TYPE_ARP = 0x0806;
+ public static final int ETHER_TYPE_IPV4 = 0x0800;
+ public static final int ETHER_TYPE_IPV6 = 0x86dd;
+ public static final int ETHER_HEADER_LEN = 14;
+
+ /**
+ * ARP constants.
+ *
+ * See also:
+ * - https://tools.ietf.org/html/rfc826
+ * - http://www.iana.org/assignments/arp-parameters/arp-parameters.xhtml
+ */
+ public static final int ARP_PAYLOAD_LEN = 28; // For Ethernet+IPv4.
+ public static final int ARP_REQUEST = 1;
+ public static final int ARP_REPLY = 2;
+ public static final int ARP_HWTYPE_RESERVED_LO = 0;
+ public static final int ARP_HWTYPE_ETHER = 1;
+ public static final int ARP_HWTYPE_RESERVED_HI = 0xffff;
+
+ /**
+ * IPv4 constants.
+ *
+ * See also:
+ * - https://tools.ietf.org/html/rfc791
+ */
+ public static final int IPV4_HEADER_MIN_LEN = 20;
+ public static final int IPV4_IHL_MASK = 0xf;
+ public static final int IPV4_FLAGS_OFFSET = 6;
+ public static final int IPV4_FRAGMENT_MASK = 0x1fff;
+ public static final int IPV4_PROTOCOL_OFFSET = 9;
+ public static final int IPV4_SRC_ADDR_OFFSET = 12;
+ public static final int IPV4_DST_ADDR_OFFSET = 16;
+ public static final int IPV4_ADDR_LEN = 4;
+
+ /**
+ * IPv6 constants.
+ *
+ * See also:
+ * - https://tools.ietf.org/html/rfc2460
+ */
+ public static final int IPV6_ADDR_LEN = 16;
+ public static final int IPV6_HEADER_LEN = 40;
+ public static final int IPV6_PROTOCOL_OFFSET = 6;
+ public static final int IPV6_SRC_ADDR_OFFSET = 8;
+ public static final int IPV6_DST_ADDR_OFFSET = 24;
+
+ /**
+ * ICMPv6 constants.
+ *
+ * See also:
+ * - https://tools.ietf.org/html/rfc4443
+ * - https://tools.ietf.org/html/rfc4861
+ */
+ public static final int ICMPV6_HEADER_MIN_LEN = 4;
+ public static final int ICMPV6_ECHO_REPLY_TYPE = 129;
+ public static final int ICMPV6_ECHO_REQUEST_TYPE = 128;
+ public static final int ICMPV6_ROUTER_SOLICITATION = 133;
+ public static final int ICMPV6_ROUTER_ADVERTISEMENT = 134;
+ public static final int ICMPV6_NEIGHBOR_SOLICITATION = 135;
+ public static final int ICMPV6_NEIGHBOR_ADVERTISEMENT = 136;
+ public static final int ICMPV6_ND_OPTION_MIN_LENGTH = 8;
+ public static final int ICMPV6_ND_OPTION_LENGTH_SCALING_FACTOR = 8;
+ public static final int ICMPV6_ND_OPTION_SLLA = 1;
+ public static final int ICMPV6_ND_OPTION_TLLA = 2;
+ public static final int ICMPV6_ND_OPTION_MTU = 5;
+
+ /**
+ * UDP constants.
+ *
+ * See also:
+ * - https://tools.ietf.org/html/rfc768
+ */
+ public static final int UDP_HEADER_LEN = 8;
+
+
+ /**
* DHCP constants.
*
* See also:
* - https://tools.ietf.org/html/rfc2131
*/
public static final int INFINITE_LEASE = 0xffffffff;
+ public static final int DHCP4_CLIENT_PORT = 68;
private NetworkStackConstants() {
throw new UnsupportedOperationException("This class is not to be instantiated");
diff --git a/packages/NetworkStack/tests/Android.bp b/packages/NetworkStack/tests/Android.bp
index bd7ff2a..45fa2dc 100644
--- a/packages/NetworkStack/tests/Android.bp
+++ b/packages/NetworkStack/tests/Android.bp
@@ -16,9 +16,12 @@
android_test {
name: "NetworkStackTests",
+ certificate: "platform",
srcs: ["src/**/*.java"],
+ resource_dirs: ["res"],
static_libs: [
"android-support-test",
+ "frameworks-base-testutils",
"mockito-target-extended-minus-junit4",
"NetworkStackLib",
"testables",
@@ -26,10 +29,70 @@
libs: [
"android.test.runner",
"android.test.base",
+ "android.test.mock",
],
jni_libs: [
// For mockito extended
"libdexmakerjvmtiagent",
"libstaticjvmtiagent",
- ]
-}
\ No newline at end of file
+ // For ApfTest
+ "libartbase",
+ "libbacktrace",
+ "libbase",
+ "libbinder",
+ "libbinderthreadstate",
+ "libc++",
+ "libcrypto",
+ "libcutils",
+ "libdexfile",
+ "libhidl-gen-utils",
+ "libhidlbase",
+ "libhidltransport",
+ "libhwbinder",
+ "liblog",
+ "liblzma",
+ "libnativehelper",
+ "libnetworkstacktestsjni",
+ "libpackagelistparser",
+ "libpcre2",
+ "libprocessgroup",
+ "libselinux",
+ "libui",
+ "libutils",
+ "libvintf",
+ "libvndksupport",
+ "libtinyxml2",
+ "libunwindstack",
+ "libutilscallstack",
+ "libziparchive",
+ "libz",
+ "netd_aidl_interface-cpp",
+ ],
+}
+
+cc_library_shared {
+ name: "libnetworkstacktestsjni",
+ srcs: [
+ "jni/**/*.cpp"
+ ],
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+ include_dirs: [
+ "hardware/google/apf",
+ ],
+ shared_libs: [
+ "libbinder",
+ "liblog",
+ "libcutils",
+ "libnativehelper",
+ "netd_aidl_interface-cpp",
+ ],
+ static_libs: [
+ "libapf",
+ "libpcap",
+ ],
+
+}
diff --git a/packages/NetworkStack/tests/AndroidManifest.xml b/packages/NetworkStack/tests/AndroidManifest.xml
index 8b8474f..9cb2c21 100644
--- a/packages/NetworkStack/tests/AndroidManifest.xml
+++ b/packages/NetworkStack/tests/AndroidManifest.xml
@@ -15,6 +15,35 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.server.networkstack.tests">
+
+ <uses-permission android:name="android.permission.READ_LOGS" />
+ <uses-permission android:name="android.permission.WRITE_SETTINGS" />
+ <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+ <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+ <uses-permission android:name="android.permission.BROADCAST_STICKY" />
+ <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" />
+ <uses-permission android:name="android.permission.MANAGE_APP_TOKENS" />
+ <uses-permission android:name="android.permission.WAKE_LOCK" />
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
+ <uses-permission android:name="android.permission.REAL_GET_TASKS" />
+ <uses-permission android:name="android.permission.GET_DETAILED_TASKS" />
+ <uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" />
+ <uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" />
+ <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
+ <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+ <uses-permission android:name="android.permission.MANAGE_USERS" />
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
+ <uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS" />
+ <uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
+ <uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
+ <uses-permission android:name="android.permission.PACKET_KEEPALIVE_OFFLOAD" />
+ <uses-permission android:name="android.permission.GET_INTENT_SENDER_INTENT" />
+ <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" />
+ <uses-permission android:name="android.permission.INSTALL_PACKAGES" />
+ <uses-permission android:name="android.permission.NETWORK_STACK" />
+
<application android:debuggable="true">
<uses-library android:name="android.test.runner" />
</application>
diff --git a/tests/net/jni/apf_jni.cpp b/packages/NetworkStack/tests/jni/apf_jni.cpp
similarity index 100%
rename from tests/net/jni/apf_jni.cpp
rename to packages/NetworkStack/tests/jni/apf_jni.cpp
diff --git a/tests/net/res/raw/apf.pcap b/packages/NetworkStack/tests/res/raw/apf.pcap
similarity index 100%
rename from tests/net/res/raw/apf.pcap
rename to packages/NetworkStack/tests/res/raw/apf.pcap
Binary files differ
diff --git a/tests/net/res/raw/apfPcap.pcap b/packages/NetworkStack/tests/res/raw/apfPcap.pcap
similarity index 100%
rename from tests/net/res/raw/apfPcap.pcap
rename to packages/NetworkStack/tests/res/raw/apfPcap.pcap
Binary files differ
diff --git a/tests/net/java/android/net/apf/ApfTest.java b/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java
similarity index 98%
rename from tests/net/java/android/net/apf/ApfTest.java
rename to packages/NetworkStack/tests/src/android/net/apf/ApfTest.java
index 3c3e7ce..f76e412 100644
--- a/tests/net/java/android/net/apf/ApfTest.java
+++ b/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java
@@ -16,8 +16,6 @@
package android.net.apf;
-import static android.net.util.NetworkConstants.ICMPV6_ECHO_REQUEST_TYPE;
-import static android.net.util.NetworkConstants.ICMPV6_ROUTER_ADVERTISEMENT;
import static android.system.OsConstants.AF_UNIX;
import static android.system.OsConstants.ARPHRD_ETHER;
import static android.system.OsConstants.ETH_P_ARP;
@@ -28,12 +26,14 @@
import static android.system.OsConstants.SOCK_STREAM;
import static com.android.internal.util.BitUtils.bytesToBEInt;
+import static com.android.server.util.NetworkStackConstants.ICMPV6_ECHO_REQUEST_TYPE;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import android.content.Context;
@@ -42,10 +42,14 @@
import android.net.apf.ApfFilter.ApfConfiguration;
import android.net.apf.ApfGenerator.IllegalInstructionException;
import android.net.apf.ApfGenerator.Register;
+import android.net.ip.IIpClientCallbacks;
+import android.net.ip.IpClient;
+import android.net.ip.IpClient.IpClientCallbacksWrapper;
import android.net.ip.IpClientCallbacks;
import android.net.metrics.IpConnectivityLog;
import android.net.metrics.RaEvent;
import android.net.util.InterfaceParams;
+import android.net.util.SharedLog;
import android.os.ConditionVariable;
import android.os.Parcelable;
import android.os.SystemClock;
@@ -57,8 +61,9 @@
import android.text.format.DateUtils;
import android.util.Log;
-import com.android.frameworks.tests.net.R;
import com.android.internal.util.HexDump;
+import com.android.server.networkstack.tests.R;
+import com.android.server.util.NetworkStackConstants;
import libcore.io.IoUtils;
import libcore.io.Streams;
@@ -100,7 +105,7 @@
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
// Load up native shared library containing APF interpreter exposed via JNI.
- System.loadLibrary("frameworksnettestsjni");
+ System.loadLibrary("networkstacktestsjni");
}
private static final String TAG = "ApfTest";
@@ -915,10 +920,14 @@
HexDump.toHexString(data, false), result);
}
- private class MockIpClientCallback extends IpClientCallbacks {
+ private class MockIpClientCallback extends IpClientCallbacksWrapper {
private final ConditionVariable mGotApfProgram = new ConditionVariable();
private byte[] mLastApfProgram;
+ MockIpClientCallback() {
+ super(mock(IIpClientCallbacks.class), mock(SharedLog.class));
+ }
+
@Override
public void installPacketFilter(byte[] filter) {
mLastApfProgram = filter;
@@ -946,7 +955,7 @@
private final long mFixedTimeMs = SystemClock.elapsedRealtime();
public TestApfFilter(Context context, ApfConfiguration config,
- IpClientCallbacks ipClientCallback, IpConnectivityLog log) throws Exception {
+ IpClientCallbacksWrapper ipClientCallback, IpConnectivityLog log) throws Exception {
super(context, config, InterfaceParams.getByName("lo"), ipClientCallback, log);
}
@@ -1075,8 +1084,8 @@
private static final byte[] IPV4_ANY_HOST_ADDR = {0, 0, 0, 0};
// Helper to initialize a default apfFilter.
- private ApfFilter setupApfFilter(IpClientCallbacks ipClientCallback, ApfConfiguration config)
- throws Exception {
+ private ApfFilter setupApfFilter(
+ IpClientCallbacksWrapper ipClientCallback, ApfConfiguration config) throws Exception {
LinkAddress link = new LinkAddress(InetAddress.getByAddress(MOCK_IPV4_ADDR), 19);
LinkProperties lp = new LinkProperties();
lp.addLinkAddress(link);
@@ -1294,7 +1303,7 @@
// However, we should still let through all other ICMPv6 types.
ByteBuffer raPacket = ByteBuffer.wrap(packet.array().clone());
- raPacket.put(ICMP6_TYPE_OFFSET, (byte)ICMPV6_ROUTER_ADVERTISEMENT);
+ raPacket.put(ICMP6_TYPE_OFFSET, (byte) NetworkStackConstants.ICMPV6_ROUTER_ADVERTISEMENT);
assertPass(ipClientCallback.getApfProgram(), raPacket.array());
// Now wake up from doze mode to ensure that we no longer drop the packets.
diff --git a/tests/net/java/android/net/apf/Bpf2Apf.java b/packages/NetworkStack/tests/src/android/net/apf/Bpf2Apf.java
similarity index 100%
rename from tests/net/java/android/net/apf/Bpf2Apf.java
rename to packages/NetworkStack/tests/src/android/net/apf/Bpf2Apf.java
diff --git a/tests/net/java/android/net/dhcp/DhcpPacketTest.java b/packages/NetworkStack/tests/src/android/net/dhcp/DhcpPacketTest.java
similarity index 100%
rename from tests/net/java/android/net/dhcp/DhcpPacketTest.java
rename to packages/NetworkStack/tests/src/android/net/dhcp/DhcpPacketTest.java
diff --git a/tests/net/java/android/net/ip/IpClientTest.java b/packages/NetworkStack/tests/src/android/net/ip/IpClientTest.java
similarity index 93%
rename from tests/net/java/android/net/ip/IpClientTest.java
rename to packages/NetworkStack/tests/src/android/net/ip/IpClientTest.java
index 5110ce1..f21809f 100644
--- a/tests/net/java/android/net/ip/IpClientTest.java
+++ b/packages/NetworkStack/tests/src/android/net/ip/IpClientTest.java
@@ -16,10 +16,13 @@
package android.net.ip;
+import static android.net.shared.LinkPropertiesParcelableUtil.fromStableParcelable;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.eq;
@@ -87,7 +90,7 @@
@Mock private INetworkManagementService mNMService;
@Mock private INetd mNetd;
@Mock private Resources mResources;
- @Mock private IpClientCallbacks mCb;
+ @Mock private IIpClientCallbacks mCb;
@Mock private AlarmManager mAlarm;
@Mock private IpClient.Dependencies mDependecies;
private MockContentResolver mContentResolver;
@@ -209,7 +212,8 @@
verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceSetEnableIPv6(iface, false);
verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceClearAddrs(iface);
verify(mCb, timeout(TEST_TIMEOUT_MS).times(1))
- .onLinkPropertiesChange(eq(makeEmptyLinkProperties(iface)));
+ .onLinkPropertiesChange(argThat(
+ lp -> fromStableParcelable(lp).equals(makeEmptyLinkProperties(iface))));
}
@Test
@@ -254,13 +258,15 @@
mObserver.addressUpdated(iface, new LinkAddress(addresses[lastAddr]));
LinkProperties want = linkproperties(links(addresses), routes(prefixes));
want.setInterfaceName(iface);
- verify(mCb, timeout(TEST_TIMEOUT_MS).times(1)).onProvisioningSuccess(eq(want));
+ verify(mCb, timeout(TEST_TIMEOUT_MS).times(1)).onProvisioningSuccess(argThat(
+ lp -> fromStableParcelable(lp).equals(want)));
ipc.shutdown();
verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceSetEnableIPv6(iface, false);
verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceClearAddrs(iface);
verify(mCb, timeout(TEST_TIMEOUT_MS).times(1))
- .onLinkPropertiesChange(eq(makeEmptyLinkProperties(iface)));
+ .onLinkPropertiesChange(argThat(
+ lp -> fromStableParcelable(lp).equals(makeEmptyLinkProperties(iface))));
}
@Test
@@ -492,11 +498,11 @@
List<String> list3 = Arrays.asList("bar", "baz");
List<String> list4 = Arrays.asList("foo", "bar", "baz");
- assertTrue(IpClient.all(list1, (x) -> false));
- assertFalse(IpClient.all(list2, (x) -> false));
- assertTrue(IpClient.all(list3, (x) -> true));
- assertTrue(IpClient.all(list2, (x) -> x.charAt(0) == 'f'));
- assertFalse(IpClient.all(list4, (x) -> x.charAt(0) == 'f'));
+ assertTrue(InitialConfiguration.all(list1, (x) -> false));
+ assertFalse(InitialConfiguration.all(list2, (x) -> false));
+ assertTrue(InitialConfiguration.all(list3, (x) -> true));
+ assertTrue(InitialConfiguration.all(list2, (x) -> x.charAt(0) == 'f'));
+ assertFalse(InitialConfiguration.all(list4, (x) -> x.charAt(0) == 'f'));
}
@Test
@@ -506,11 +512,11 @@
List<String> list3 = Arrays.asList("bar", "baz");
List<String> list4 = Arrays.asList("foo", "bar", "baz");
- assertFalse(IpClient.any(list1, (x) -> true));
- assertTrue(IpClient.any(list2, (x) -> true));
- assertTrue(IpClient.any(list2, (x) -> x.charAt(0) == 'f'));
- assertFalse(IpClient.any(list3, (x) -> x.charAt(0) == 'f'));
- assertTrue(IpClient.any(list4, (x) -> x.charAt(0) == 'f'));
+ assertFalse(InitialConfiguration.any(list1, (x) -> true));
+ assertTrue(InitialConfiguration.any(list2, (x) -> true));
+ assertTrue(InitialConfiguration.any(list2, (x) -> x.charAt(0) == 'f'));
+ assertFalse(InitialConfiguration.any(list3, (x) -> x.charAt(0) == 'f'));
+ assertTrue(InitialConfiguration.any(list4, (x) -> x.charAt(0) == 'f'));
}
@Test
diff --git a/tests/net/java/android/net/ip/IpReachabilityMonitorTest.java b/packages/NetworkStack/tests/src/android/net/ip/IpReachabilityMonitorTest.java
similarity index 100%
rename from tests/net/java/android/net/ip/IpReachabilityMonitorTest.java
rename to packages/NetworkStack/tests/src/android/net/ip/IpReachabilityMonitorTest.java
diff --git a/tests/net/java/android/net/util/ConnectivityPacketSummaryTest.java b/packages/NetworkStack/tests/src/android/net/util/ConnectivityPacketSummaryTest.java
similarity index 100%
rename from tests/net/java/android/net/util/ConnectivityPacketSummaryTest.java
rename to packages/NetworkStack/tests/src/android/net/util/ConnectivityPacketSummaryTest.java
diff --git a/tests/net/java/android/net/util/PacketReaderTest.java b/packages/NetworkStack/tests/src/android/net/util/PacketReaderTest.java
similarity index 100%
rename from tests/net/java/android/net/util/PacketReaderTest.java
rename to packages/NetworkStack/tests/src/android/net/util/PacketReaderTest.java
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index de86789..dbeee1c 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -40,11 +40,8 @@
<bool name="def_wifi_display_on">false</bool>
<bool name="def_install_non_market_apps">false</bool>
<bool name="def_package_verifier_enable">true</bool>
- <!-- Comma-separated list of location providers.
- Network location is off by default because it requires
- user opt-in via Setup Wizard or Settings.
- -->
- <string name="def_location_providers_allowed" translatable="false">gps</string>
+ <!-- Comma-separated list of location providers -->
+ <string name="def_location_providers_allowed" translatable="false">gps,network</string>
<bool name="assisted_gps_enabled">true</bool>
<bool name="def_netstats_enabled">true</bool>
<bool name="def_usb_mass_storage_enabled">true</bool>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 5105ff4..4453121 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -3232,7 +3232,7 @@
}
private final class UpgradeController {
- private static final int SETTINGS_VERSION = 172;
+ private static final int SETTINGS_VERSION = 173;
private final int mUserId;
@@ -4217,6 +4217,41 @@
currentVersion = 172;
}
+ if (currentVersion == 172) {
+ // Version 172: Set the default value for Secure Settings: LOCATION_MODE
+
+ final SettingsState secureSettings = getSecureSettingsLocked(userId);
+
+ final Setting locationMode = secureSettings.getSettingLocked(
+ Secure.LOCATION_MODE);
+
+ if (locationMode.isNull()) {
+ final Setting locationProvidersAllowed = secureSettings.getSettingLocked(
+ Secure.LOCATION_PROVIDERS_ALLOWED);
+
+ String defLocationMode = Integer.toString(
+ !TextUtils.isEmpty(locationProvidersAllowed.getValue())
+ ? Secure.LOCATION_MODE_HIGH_ACCURACY
+ : Secure.LOCATION_MODE_OFF);
+ secureSettings.insertSettingLocked(
+ Secure.LOCATION_MODE, defLocationMode,
+ null, true, SettingsState.SYSTEM_PACKAGE_NAME);
+
+ // also reset LOCATION_PROVIDERS_ALLOWED back to the default value - this
+ // setting is now only for debug/test purposes, and will likely be removed
+ // in a later release. LocationManagerService is responsible for adjusting
+ // these settings to the proper state.
+
+ String defLocationProvidersAllowed = getContext().getResources().getString(
+ R.string.def_location_providers_allowed);
+ secureSettings.insertSettingLocked(
+ Secure.LOCATION_PROVIDERS_ALLOWED, defLocationProvidersAllowed,
+ null, true, SettingsState.SYSTEM_PACKAGE_NAME);
+ }
+
+ currentVersion = 173;
+ }
+
// vXXX: Add new settings above this point.
if (currentVersion != newVersion) {
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 2d7471d..a9ff21f 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -1962,8 +1962,7 @@
}
@Override
- public void onFinished(long durationMs, String title, String description)
- throws RemoteException {
+ public void onFinished() throws RemoteException {
// TODO(b/111441001): implement
}
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index d64e2f9..13d27bb 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2279,7 +2279,7 @@
app for debugging. Will not be seen by users. [CHAR LIMIT=20] -->
<string name="heap_dump_tile_name">Dump SysUI Heap</string>
- <!-- Text on chip for multiple apps using a single app op [CHAR LIMIT=10] -->
+ <!-- Text on chip for multiple apps using a single app op [CHAR LIMIT=12] -->
<plurals name="ongoing_privacy_chip_multiple_apps">
<item quantity="one"><xliff:g id="num_apps" example="1">%d</xliff:g> app</item>
<item quantity="few"><xliff:g id="num_apps" example="3">%d</xliff:g> apps</item>
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index 1155a41..e1becdb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -188,7 +188,7 @@
state.secondaryLabel = r.getString(R.string.status_bar_airplane);
} else if (mobileDataEnabled) {
state.state = Tile.STATE_ACTIVE;
- state.secondaryLabel = getMobileDataDescription(cb);
+ state.secondaryLabel = getMobileDataSubscriptionName(cb);
} else {
state.state = Tile.STATE_INACTIVE;
state.secondaryLabel = r.getString(R.string.cell_data_off);
@@ -207,16 +207,16 @@
state.contentDescription = state.label + ", " + contentDescriptionSuffix;
}
- private CharSequence getMobileDataDescription(CallbackInfo cb) {
- if (cb.roaming && !TextUtils.isEmpty(cb.dataContentDescription)) {
+ private CharSequence getMobileDataSubscriptionName(CallbackInfo cb) {
+ if (cb.roaming && !TextUtils.isEmpty(cb.dataSubscriptionName)) {
String roaming = mContext.getString(R.string.data_connection_roaming);
- String dataDescription = cb.dataContentDescription;
+ String dataDescription = cb.dataSubscriptionName.toString();
return mContext.getString(R.string.mobile_data_text_format, roaming, dataDescription);
}
if (cb.roaming) {
return mContext.getString(R.string.data_connection_roaming);
}
- return cb.dataContentDescription;
+ return cb.dataSubscriptionName;
}
@Override
@@ -231,7 +231,7 @@
private static final class CallbackInfo {
boolean airplaneModeEnabled;
- String dataContentDescription;
+ CharSequence dataSubscriptionName;
boolean activityIn;
boolean activityOut;
boolean noSim;
@@ -249,7 +249,7 @@
// Not data sim, don't display.
return;
}
- mInfo.dataContentDescription = typeContentDescription;
+ mInfo.dataSubscriptionName = mController.getMobileDataNetworkName();
mInfo.activityIn = activityIn;
mInfo.activityOut = activityOut;
mInfo.roaming = roaming;
diff --git a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
index 4a1e5b9..2241569 100644
--- a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
+++ b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
@@ -164,7 +164,7 @@
int N = pkgs.size();
for (int a = N-1; a >= 0; a--) {
PackageInfo pkg = pkgs.get(a);
- if (!AppBackupUtils.appIsEligibleForBackup(pkg.applicationInfo, pm)) {
+ if (!AppBackupUtils.appIsEligibleForBackup(pkg.applicationInfo, userId)) {
pkgs.remove(a);
}
}
diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java
index b9a6f3c..4f58d79 100644
--- a/services/backup/java/com/android/server/backup/Trampoline.java
+++ b/services/backup/java/com/android/server/backup/Trampoline.java
@@ -636,14 +636,18 @@
}
@Override
- public void opComplete(int token, long result) throws RemoteException {
- int userId = binderGetCallingUserId();
+ public void opCompleteForUser(int userId, int token, long result) throws RemoteException {
if (isUserReadyForBackup(userId)) {
- mService.opComplete(binderGetCallingUserId(), token, result);
+ mService.opComplete(userId, token, result);
}
}
@Override
+ public void opComplete(int token, long result) throws RemoteException {
+ opCompleteForUser(binderGetCallingUserId(), token, result);
+ }
+
+ @Override
public long getAvailableRestoreTokenForUser(int userId, String packageName) {
return isUserReadyForBackup(userId) ? mService.getAvailableRestoreToken(userId,
packageName) : 0;
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 79f8a7e..115e924 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -804,7 +804,7 @@
public BackupAgent makeMetadataAgent() {
PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(mPackageManager, mUserId);
pmAgent.attach(mContext);
- pmAgent.onCreate();
+ pmAgent.onCreate(UserHandle.of(mUserId));
return pmAgent;
}
@@ -815,7 +815,7 @@
PackageManagerBackupAgent pmAgent =
new PackageManagerBackupAgent(mPackageManager, packages, mUserId);
pmAgent.attach(mContext);
- pmAgent.onCreate();
+ pmAgent.onCreate(UserHandle.of(mUserId));
return pmAgent;
}
@@ -910,10 +910,10 @@
long lastBackup = in.readLong();
foundApps.add(pkgName); // all apps that we've addressed already
try {
- PackageInfo pkg = mPackageManager.getPackageInfo(pkgName, 0);
+ PackageInfo pkg = mPackageManager.getPackageInfoAsUser(pkgName, 0, mUserId);
if (AppBackupUtils.appGetsFullBackup(pkg)
- && AppBackupUtils.appIsEligibleForBackup(
- pkg.applicationInfo, mPackageManager)) {
+ && AppBackupUtils.appIsEligibleForBackup(pkg.applicationInfo,
+ mUserId)) {
schedule.add(new FullBackupEntry(pkgName, lastBackup));
} else {
if (DEBUG) {
@@ -933,8 +933,8 @@
// scan to make sure that we're tracking all full-backup candidates properly
for (PackageInfo app : apps) {
if (AppBackupUtils.appGetsFullBackup(app)
- && AppBackupUtils.appIsEligibleForBackup(
- app.applicationInfo, mPackageManager)) {
+ && AppBackupUtils.appIsEligibleForBackup(app.applicationInfo,
+ mUserId)) {
if (!foundApps.contains(app.packageName)) {
if (MORE_DEBUG) {
Slog.i(TAG, "New full backup app " + app.packageName + " found");
@@ -960,7 +960,7 @@
schedule = new ArrayList<>(apps.size());
for (PackageInfo info : apps) {
if (AppBackupUtils.appGetsFullBackup(info) && AppBackupUtils.appIsEligibleForBackup(
- info.applicationInfo, mPackageManager)) {
+ info.applicationInfo, mUserId)) {
schedule.add(new FullBackupEntry(info.packageName, 0));
}
}
@@ -1222,8 +1222,8 @@
mPackageManager.getPackageInfoAsUser(
packageName, /* flags */ 0, mUserId);
if (AppBackupUtils.appGetsFullBackup(app)
- && AppBackupUtils.appIsEligibleForBackup(
- app.applicationInfo, mPackageManager)) {
+ && AppBackupUtils.appIsEligibleForBackup(app.applicationInfo,
+ mUserId)) {
enqueueFullBackup(packageName, now);
scheduleNextFullBackupJob(0);
} else {
@@ -1618,8 +1618,7 @@
try {
PackageInfo packageInfo = mPackageManager.getPackageInfoAsUser(packageName,
PackageManager.GET_SIGNING_CERTIFICATES, mUserId);
- if (!AppBackupUtils.appIsEligibleForBackup(packageInfo.applicationInfo,
- mPackageManager)) {
+ if (!AppBackupUtils.appIsEligibleForBackup(packageInfo.applicationInfo, mUserId)) {
BackupObserverUtils.sendBackupOnPackageResult(observer, packageName,
BackupManager.ERROR_BACKUP_NOT_ALLOWED);
continue;
@@ -2095,7 +2094,8 @@
}
try {
- PackageInfo appInfo = mPackageManager.getPackageInfo(entry.packageName, 0);
+ PackageInfo appInfo = mPackageManager.getPackageInfoAsUser(
+ entry.packageName, 0, mUserId);
if (!AppBackupUtils.appGetsFullBackup(appInfo)) {
// The head app isn't supposed to get full-data backups [any more];
// so we cull it and force a loop around to consider the new head
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
index 31786d7..0a7159b 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
@@ -286,7 +286,8 @@
Iterator<Entry<String, PackageInfo>> iter = packagesToBackup.entrySet().iterator();
while (iter.hasNext()) {
PackageInfo pkg = iter.next().getValue();
- if (!AppBackupUtils.appIsEligibleForBackup(pkg.applicationInfo, pm)
+ if (!AppBackupUtils.appIsEligibleForBackup(pkg.applicationInfo,
+ mUserBackupManagerService.getUserId())
|| AppBackupUtils.appIsStopped(pkg.applicationInfo)) {
iter.remove();
if (DEBUG) {
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
index 0fb4f93..86e679f 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
@@ -143,6 +143,7 @@
private final int mBackupRunnerOpToken;
private final OnTaskFinishedListener mListener;
private final TransportClient mTransportClient;
+ private final int mUserId;
// This is true when a backup operation for some package is in progress.
private volatile boolean mIsDoingBackup;
@@ -173,6 +174,7 @@
mAgentTimeoutParameters = Preconditions.checkNotNull(
backupManagerService.getAgentTimeoutParameters(),
"Timeout parameters cannot be null");
+ mUserId = backupManagerService.getUserId();
if (backupManagerService.isBackupOperationInProgress()) {
if (DEBUG) {
@@ -187,9 +189,10 @@
for (String pkg : whichPackages) {
try {
PackageManager pm = backupManagerService.getPackageManager();
- PackageInfo info = pm.getPackageInfo(pkg, PackageManager.GET_SIGNING_CERTIFICATES);
+ PackageInfo info = pm.getPackageInfoAsUser(pkg,
+ PackageManager.GET_SIGNING_CERTIFICATES, mUserId);
mCurrentPackage = info;
- if (!AppBackupUtils.appIsEligibleForBackup(info.applicationInfo, pm)) {
+ if (!AppBackupUtils.appIsEligibleForBackup(info.applicationInfo, mUserId)) {
// Cull any packages that have indicated that backups are not permitted,
// that run as system-domain uids but do not define their own backup agents,
// as well as any explicit mention of the 'special' shared-storage agent
@@ -633,7 +636,7 @@
unregisterTask();
if (mJob != null) {
- mJob.finishBackupPass(backupManagerService.getUserId());
+ mJob.finishBackupPass(mUserId);
}
synchronized (backupManagerService.getQueueLock()) {
diff --git a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
index cfc129e..294eb01 100644
--- a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
+++ b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
@@ -489,7 +489,7 @@
throw AgentException.permanent(e);
}
ApplicationInfo applicationInfo = packageInfo.applicationInfo;
- if (!AppBackupUtils.appIsEligibleForBackup(applicationInfo, mPackageManager)) {
+ if (!AppBackupUtils.appIsEligibleForBackup(applicationInfo, mUserId)) {
mReporter.onPackageNotEligibleForBackup(packageName);
throw AgentException.permanent();
}
diff --git a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
index b3d9fbc..c5389fa 100644
--- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
@@ -230,7 +230,7 @@
PackageManagerInternal.class);
RestorePolicy restorePolicy = tarBackupReader.chooseRestorePolicy(
mBackupManagerService.getPackageManager(), allowApks, info, signatures,
- pmi);
+ pmi, mUserId);
mManifestSignatures.put(info.packageName, signatures);
mPackagePolicies.put(pkg, restorePolicy);
mPackageInstallers.put(pkg, info.installerPackageName);
@@ -332,8 +332,9 @@
}
try {
- mTargetApp = mBackupManagerService.getPackageManager()
- .getApplicationInfoAsUser(pkg, 0, mUserId);
+ mTargetApp =
+ mBackupManagerService.getPackageManager()
+ .getApplicationInfoAsUser(pkg, 0, mUserId);
// If we haven't sent any data to this app yet, we probably
// need to clear it first. Check that.
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index d01f77b..7763d7b 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -232,7 +232,7 @@
continue;
}
- if (AppBackupUtils.appIsEligibleForBackup(info.applicationInfo, pm)) {
+ if (AppBackupUtils.appIsEligibleForBackup(info.applicationInfo, mUserId)) {
mAcceptSet.add(info);
}
} catch (NameNotFoundException e) {
diff --git a/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java b/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
index 054879b..2db8928 100644
--- a/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
+++ b/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
@@ -18,19 +18,25 @@
import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
import static com.android.server.backup.BackupManagerService.TAG;
+import static com.android.server.backup.UserBackupManagerService.PACKAGE_MANAGER_SENTINEL;
import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
import android.annotation.Nullable;
+import android.app.AppGlobals;
import android.app.backup.BackupTransport;
import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.Signature;
import android.content.pm.SigningInfo;
import android.os.Process;
+import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.backup.IBackupTransport;
import com.android.internal.util.ArrayUtils;
import com.android.server.backup.transport.TransportClient;
@@ -39,7 +45,6 @@
* Utility methods wrapping operations on ApplicationInfo and PackageInfo.
*/
public class AppBackupUtils {
-
private static final boolean DEBUG = false;
/**
@@ -54,15 +59,30 @@
* <li>it is the special shared-storage backup package used for 'adb backup'
* </ol>
*/
- public static boolean appIsEligibleForBackup(ApplicationInfo app, PackageManager pm) {
+ public static boolean appIsEligibleForBackup(ApplicationInfo app, int userId) {
+ return appIsEligibleForBackup(app, AppGlobals.getPackageManager(), userId);
+ }
+
+ @VisibleForTesting
+ static boolean appIsEligibleForBackup(ApplicationInfo app,
+ IPackageManager packageManager, int userId) {
// 1. their manifest states android:allowBackup="false"
if ((app.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) {
return false;
}
- // 2. they run as a system-level uid but do not supply their own backup agent
- if ((app.uid < Process.FIRST_APPLICATION_UID) && (app.backupAgentName == null)) {
- return false;
+ // 2. they run as a system-level uid
+ if ((app.uid < Process.FIRST_APPLICATION_UID)) {
+ // and the backup is happening for non-system user
+ if (userId != UserHandle.USER_SYSTEM && !app.packageName.equals(
+ PACKAGE_MANAGER_SENTINEL)) {
+ return false;
+ }
+
+ // or do not supply their own backup agent
+ if (app.backupAgentName == null) {
+ return false;
+ }
}
// 3. it is the special shared-storage backup package used for 'adb backup'
@@ -75,9 +95,7 @@
return false;
}
- // Everything else checks out; the only remaining roadblock would be if the
- // package were disabled
- return !appIsDisabled(app, pm);
+ return !appIsDisabled(app, packageManager, userId);
}
/**
@@ -99,9 +117,9 @@
PackageInfo packageInfo = pm.getPackageInfoAsUser(packageName,
PackageManager.GET_SIGNING_CERTIFICATES, userId);
ApplicationInfo applicationInfo = packageInfo.applicationInfo;
- if (!appIsEligibleForBackup(applicationInfo, pm)
+ if (!appIsEligibleForBackup(applicationInfo, userId)
|| appIsStopped(applicationInfo)
- || appIsDisabled(applicationInfo, pm)) {
+ || appIsDisabled(applicationInfo, userId)) {
return false;
}
if (transportClient != null) {
@@ -123,8 +141,22 @@
}
/** Avoid backups of 'disabled' apps. */
- public static boolean appIsDisabled(ApplicationInfo app, PackageManager pm) {
- switch (pm.getApplicationEnabledSetting(app.packageName)) {
+ static boolean appIsDisabled(ApplicationInfo app, int userId) {
+ return appIsDisabled(app, AppGlobals.getPackageManager(), userId);
+ }
+
+ @VisibleForTesting
+ static boolean appIsDisabled(ApplicationInfo app,
+ IPackageManager packageManager, int userId) {
+ int enabledSetting;
+ try {
+ enabledSetting = packageManager.getApplicationEnabledSetting(app.packageName, userId);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to get application enabled setting: " + e);
+ return false;
+ }
+
+ switch (enabledSetting) {
case PackageManager.COMPONENT_ENABLED_STATE_DISABLED:
case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER:
case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED:
diff --git a/services/backup/java/com/android/server/backup/utils/TarBackupReader.java b/services/backup/java/com/android/server/backup/utils/TarBackupReader.java
index 0f4b681..f4b235a 100644
--- a/services/backup/java/com/android/server/backup/utils/TarBackupReader.java
+++ b/services/backup/java/com/android/server/backup/utils/TarBackupReader.java
@@ -383,11 +383,12 @@
* @param allowApks - allow restore set to include apks.
* @param info - file metadata.
* @param signatures - array of signatures parsed from backup file.
+ * @param userId - ID of the user for which restore is performed.
* @return a restore policy constant.
*/
public RestorePolicy chooseRestorePolicy(PackageManager packageManager,
boolean allowApks, FileMetadata info, Signature[] signatures,
- PackageManagerInternal pmi) {
+ PackageManagerInternal pmi, int userId) {
if (signatures == null) {
return RestorePolicy.IGNORE;
}
@@ -396,8 +397,8 @@
// Okay, got the manifest info we need...
try {
- PackageInfo pkgInfo = packageManager.getPackageInfo(
- info.packageName, PackageManager.GET_SIGNING_CERTIFICATES);
+ PackageInfo pkgInfo = packageManager.getPackageInfoAsUser(
+ info.packageName, PackageManager.GET_SIGNING_CERTIFICATES, userId);
// Fall through to IGNORE if the app explicitly disallows backup
final int flags = pkgInfo.applicationInfo.flags;
if ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) {
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index fae7a8d..aa74600 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -100,7 +100,7 @@
import android.net.shared.NetworkMonitorUtils;
import android.net.shared.PrivateDnsConfig;
import android.net.util.MultinetworkPolicyTracker;
-import android.net.util.NetdService;
+import android.net.shared.NetdService;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
@@ -2640,8 +2640,7 @@
}
private boolean networkRequiresValidation(NetworkAgentInfo nai) {
- return isValidationRequired(
- mDefaultRequest.networkCapabilities, nai.networkCapabilities);
+ return isValidationRequired(nai.networkCapabilities);
}
private void handleFreshlyValidatedNetwork(NetworkAgentInfo nai) {
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index 126bf65..371276f 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -44,7 +44,7 @@
import android.net.Network;
import android.net.NetworkUtils;
import android.net.TrafficStats;
-import android.net.util.NetdService;
+import android.net.shared.NetdService;
import android.os.Binder;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index b3a0f64..8dcc1d5 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -849,6 +849,15 @@
mAllowed = !mIsManagedBySettings;
mEnabled = false;
mProperties = null;
+
+ if (mIsManagedBySettings) {
+ // since we assume providers are disabled by default
+ Settings.Secure.putStringForUser(
+ mContext.getContentResolver(),
+ Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
+ "-" + mName,
+ mCurrentUserId);
+ }
}
@GuardedBy("mLock")
@@ -2900,33 +2909,11 @@
long identity = Binder.clearCallingIdentity();
try {
- boolean enabled;
- try {
- enabled = Settings.Secure.getIntForUser(
+ return Settings.Secure.getIntForUser(
mContext.getContentResolver(),
Settings.Secure.LOCATION_MODE,
+ Settings.Secure.LOCATION_MODE_OFF,
userId) != Settings.Secure.LOCATION_MODE_OFF;
- } catch (Settings.SettingNotFoundException e) {
- // OS upgrade case where mode isn't set yet
- enabled = !TextUtils.isEmpty(Settings.Secure.getStringForUser(
- mContext.getContentResolver(),
- Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
- userId));
-
- try {
- Settings.Secure.putIntForUser(
- mContext.getContentResolver(),
- Settings.Secure.LOCATION_MODE,
- enabled
- ? Settings.Secure.LOCATION_MODE_HIGH_ACCURACY
- : Settings.Secure.LOCATION_MODE_OFF,
- userId);
- } catch (RuntimeException ex) {
- // any problem with writing should not be propagated
- Slog.e(TAG, "error updating location mode", ex);
- }
- }
- return enabled;
} finally {
Binder.restoreCallingIdentity(identity);
}
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 2ce2f8d..89ff338 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -62,7 +62,7 @@
import android.net.RouteInfo;
import android.net.TetherStatsParcel;
import android.net.UidRange;
-import android.net.util.NetdService;
+import android.net.shared.NetdService;
import android.os.BatteryStats;
import android.os.Binder;
import android.os.Handler;
diff --git a/services/core/java/com/android/server/adb/AdbDebuggingManager.java b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
index ccead6c..c7b9a3c 100644
--- a/services/core/java/com/android/server/adb/AdbDebuggingManager.java
+++ b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
@@ -18,6 +18,7 @@
import static com.android.internal.util.dump.DumpUtils.writeStringIfNotNull;
+import android.annotation.TestApi;
import android.app.ActivityManager;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
@@ -26,6 +27,7 @@
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.content.res.Resources;
+import android.debug.AdbProtoEnums;
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
import android.os.Environment;
@@ -37,21 +39,36 @@
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
+import android.provider.Settings;
import android.service.adb.AdbDebuggingManagerProto;
+import android.util.AtomicFile;
import android.util.Base64;
import android.util.Slog;
+import android.util.StatsLog;
+import android.util.Xml;
import com.android.internal.R;
+import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.XmlUtils;
import com.android.internal.util.dump.DualDumpOutputStream;
import com.android.server.FgThread;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
import java.io.File;
+import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
/**
* Provides communication to the Android Debug Bridge daemon to allow, deny, or clear public keysi
@@ -63,6 +80,7 @@
private static final String ADBD_SOCKET = "adbd";
private static final String ADB_DIRECTORY = "misc/adb";
+ // This file contains keys that will always be allowed to connect to the device via adb.
private static final String ADB_KEYS_FILE = "adb_keys";
private static final int BUFFER_SIZE = 4096;
@@ -71,12 +89,25 @@
private AdbDebuggingThread mThread;
private boolean mAdbEnabled = false;
private String mFingerprints;
+ private String mConnectedKey;
+ private String mConfirmComponent;
public AdbDebuggingManager(Context context) {
mHandler = new AdbDebuggingHandler(FgThread.get().getLooper());
mContext = context;
}
+ /**
+ * Constructor that accepts the component to be invoked to confirm if the user wants to allow
+ * an adb connection from the key.
+ */
+ @TestApi
+ protected AdbDebuggingManager(Context context, String confirmComponent) {
+ mHandler = new AdbDebuggingHandler(FgThread.get().getLooper());
+ mContext = context;
+ mConfirmComponent = confirmComponent;
+ }
+
class AdbDebuggingThread extends Thread {
private boolean mStopped;
private LocalSocket mSocket;
@@ -135,7 +166,9 @@
byte[] buffer = new byte[BUFFER_SIZE];
while (true) {
int count = mInputStream.read(buffer);
- if (count < 0) {
+ // if less than 2 bytes are read the if statements below will throw an
+ // IndexOutOfBoundsException.
+ if (count < 2) {
break;
}
@@ -146,6 +179,11 @@
AdbDebuggingHandler.MESSAGE_ADB_CONFIRM);
msg.obj = key;
mHandler.sendMessage(msg);
+ } else if (buffer[0] == 'D' && buffer[1] == 'C') {
+ Slog.d(TAG, "Received disconnected message");
+ Message msg = mHandler.obtainMessage(
+ AdbDebuggingHandler.MESSAGE_ADB_DISCONNECT);
+ mHandler.sendMessage(msg);
} else {
Slog.e(TAG, "Wrong message: "
+ (new String(Arrays.copyOfRange(buffer, 0, 2))));
@@ -202,15 +240,41 @@
}
class AdbDebuggingHandler extends Handler {
- private static final int MESSAGE_ADB_ENABLED = 1;
- private static final int MESSAGE_ADB_DISABLED = 2;
- private static final int MESSAGE_ADB_ALLOW = 3;
- private static final int MESSAGE_ADB_DENY = 4;
- private static final int MESSAGE_ADB_CONFIRM = 5;
- private static final int MESSAGE_ADB_CLEAR = 6;
+ // The time to schedule the job to keep the key store updated with a currently connected
+ // key. This job is required because a deveoper could keep a device connected to their
+ // system beyond the time within which a subsequent connection is allowed. But since the
+ // last connection time is only written when a device is connected and disconnected then
+ // if the device is rebooted while connected to the development system it would appear as
+ // though the adb grant for the system is no longer authorized and the developer would need
+ // to manually allow the connection again.
+ private static final long UPDATE_KEY_STORE_JOB_INTERVAL = 86400000;
+
+ static final int MESSAGE_ADB_ENABLED = 1;
+ static final int MESSAGE_ADB_DISABLED = 2;
+ static final int MESSAGE_ADB_ALLOW = 3;
+ static final int MESSAGE_ADB_DENY = 4;
+ static final int MESSAGE_ADB_CONFIRM = 5;
+ static final int MESSAGE_ADB_CLEAR = 6;
+ static final int MESSAGE_ADB_DISCONNECT = 7;
+ static final int MESSAGE_ADB_PERSIST_KEY_STORE = 8;
+ static final int MESSAGE_ADB_UPDATE_KEY_CONNECTION_TIME = 9;
+
+ private AdbKeyStore mAdbKeyStore;
AdbDebuggingHandler(Looper looper) {
super(looper);
+ mAdbKeyStore = new AdbKeyStore();
+ }
+
+ /**
+ * Constructor that accepts the AdbDebuggingThread to which responses should be sent
+ * and the AdbKeyStore to be used to store the temporary grants.
+ */
+ @TestApi
+ AdbDebuggingHandler(Looper looper, AdbDebuggingThread thread, AdbKeyStore adbKeyStore) {
+ super(looper);
+ mThread = thread;
+ mAdbKeyStore = adbKeyStore;
}
public void handleMessage(Message msg) {
@@ -251,12 +315,15 @@
break;
}
- if (msg.arg1 == 1) {
- writeKey(key);
- }
-
+ boolean alwaysAllow = msg.arg1 == 1;
if (mThread != null) {
mThread.sendResponse("OK");
+ if (alwaysAllow) {
+ mConnectedKey = key;
+ mAdbKeyStore.setLastConnectionTime(key, System.currentTimeMillis());
+ scheduleJobToUpdateAdbKeyStore();
+ }
+ logAdbConnectionChanged(key, AdbProtoEnums.USER_ALLOWED, alwaysAllow);
}
break;
}
@@ -264,36 +331,88 @@
case MESSAGE_ADB_DENY:
if (mThread != null) {
mThread.sendResponse("NO");
+ logAdbConnectionChanged(null, AdbProtoEnums.USER_DENIED, false);
}
break;
case MESSAGE_ADB_CONFIRM: {
+ String key = (String) msg.obj;
if ("trigger_restart_min_framework".equals(
SystemProperties.get("vold.decrypt"))) {
Slog.d(TAG, "Deferring adb confirmation until after vold decrypt");
if (mThread != null) {
mThread.sendResponse("NO");
+ logAdbConnectionChanged(key, AdbProtoEnums.DENIED_VOLD_DECRYPT, false);
}
break;
}
- String key = (String) msg.obj;
String fingerprints = getFingerprints(key);
if ("".equals(fingerprints)) {
if (mThread != null) {
mThread.sendResponse("NO");
+ logAdbConnectionChanged(key, AdbProtoEnums.DENIED_INVALID_KEY, false);
}
break;
}
- mFingerprints = fingerprints;
- startConfirmation(key, mFingerprints);
+ // Check if the key should be allowed without user interaction.
+ if (mAdbKeyStore.isKeyAuthorized(key)) {
+ if (mThread != null) {
+ mThread.sendResponse("OK");
+ mAdbKeyStore.setLastConnectionTime(key, System.currentTimeMillis());
+ logAdbConnectionChanged(key, AdbProtoEnums.AUTOMATICALLY_ALLOWED, true);
+ mConnectedKey = key;
+ scheduleJobToUpdateAdbKeyStore();
+ }
+ } else {
+ logAdbConnectionChanged(key, AdbProtoEnums.AWAITING_USER_APPROVAL, false);
+ mFingerprints = fingerprints;
+ startConfirmation(key, mFingerprints);
+ }
break;
}
- case MESSAGE_ADB_CLEAR:
+ case MESSAGE_ADB_CLEAR: {
deleteKeyFile();
+ mConnectedKey = null;
+ mAdbKeyStore.deleteKeyStore();
+ cancelJobToUpdateAdbKeyStore();
break;
+ }
+
+ case MESSAGE_ADB_DISCONNECT: {
+ if (mConnectedKey != null) {
+ mAdbKeyStore.setLastConnectionTime(mConnectedKey,
+ System.currentTimeMillis());
+ cancelJobToUpdateAdbKeyStore();
+ }
+ logAdbConnectionChanged(mConnectedKey, AdbProtoEnums.DISCONNECTED,
+ (mConnectedKey != null));
+ mConnectedKey = null;
+ break;
+ }
+
+ case MESSAGE_ADB_PERSIST_KEY_STORE: {
+ mAdbKeyStore.persistKeyStore();
+ break;
+ }
+
+ case MESSAGE_ADB_UPDATE_KEY_CONNECTION_TIME: {
+ if (mConnectedKey != null) {
+ mAdbKeyStore.setLastConnectionTime(mConnectedKey,
+ System.currentTimeMillis());
+ scheduleJobToUpdateAdbKeyStore();
+ }
+ break;
+ }
}
}
+
+ private void logAdbConnectionChanged(String key, int state, boolean alwaysAllow) {
+ long lastConnectionTime = mAdbKeyStore.getLastConnectionTime(key);
+ long authWindow = mAdbKeyStore.getAllowedConnectionTime();
+ StatsLog.write(StatsLog.ADB_CONNECTION_CHANGED, lastConnectionTime, authWindow, state,
+ alwaysAllow);
+ }
}
private String getFingerprints(String key) {
@@ -335,7 +454,8 @@
UserInfo userInfo = UserManager.get(mContext).getUserInfo(currentUserId);
String componentString;
if (userInfo.isAdmin()) {
- componentString = Resources.getSystem().getString(
+ componentString = mConfirmComponent != null
+ ? mConfirmComponent : Resources.getSystem().getString(
com.android.internal.R.string.config_customAdbPublicKeyConfirmationComponent);
} else {
// If the current foreground user is not the admin user we send a different
@@ -397,7 +517,10 @@
return intent;
}
- private File getUserKeyFile() {
+ /**
+ * Returns a new File with the specified name in the adb directory.
+ */
+ private File getAdbFile(String fileName) {
File dataDir = Environment.getDataDirectory();
File adbDir = new File(dataDir, ADB_DIRECTORY);
@@ -406,7 +529,11 @@
return null;
}
- return new File(adbDir, ADB_KEYS_FILE);
+ return new File(adbDir, fileName);
+ }
+
+ private File getUserKeyFile() {
+ return getAdbFile(ADB_KEYS_FILE);
}
private void writeKey(String key) {
@@ -476,6 +603,36 @@
}
/**
+ * Sends a message to the handler to persist the key store.
+ */
+ private void sendPersistKeyStoreMessage() {
+ Message msg = mHandler.obtainMessage(AdbDebuggingHandler.MESSAGE_ADB_PERSIST_KEY_STORE);
+ mHandler.sendMessage(msg);
+ }
+
+ /**
+ * Schedules a job to update the connection time of the currently connected key. This is
+ * intended for cases such as development devices that are left connected to a user's
+ * system beyond the window within which a connection is allowed without user interaction.
+ * A job should be rescheduled daily so that if the device is rebooted while connected to
+ * the user's system the last time in the key store will show within 24 hours which should
+ * be within the allowed window.
+ */
+ private void scheduleJobToUpdateAdbKeyStore() {
+ Message message = mHandler.obtainMessage(
+ AdbDebuggingHandler.MESSAGE_ADB_UPDATE_KEY_CONNECTION_TIME);
+ mHandler.sendMessageDelayed(message, AdbDebuggingHandler.UPDATE_KEY_STORE_JOB_INTERVAL);
+ }
+
+ /**
+ * Cancels the scheduled job to update the connection time of the currently connected key.
+ * This should be invoked once the adb session is disconnected.
+ */
+ private void cancelJobToUpdateAdbKeyStore() {
+ mHandler.removeMessages(AdbDebuggingHandler.MESSAGE_ADB_UPDATE_KEY_CONNECTION_TIME);
+ }
+
+ /**
* Dump the USB debugging state.
*/
public void dump(DualDumpOutputStream dump, String idName, long id) {
@@ -501,4 +658,231 @@
dump.end(token);
}
+
+ /**
+ * Handles adb keys for which the user has granted the 'always allow' option. This class ensures
+ * these grants are revoked after a period of inactivity as specified in the
+ * ADB_ALLOWED_CONNECTION_TIME setting.
+ */
+ class AdbKeyStore {
+ private Map<String, Long> mKeyMap;
+ private File mKeyFile;
+ private AtomicFile mAtomicKeyFile;
+ // This file contains keys that will be allowed to connect without user interaction as long
+ // as a subsequent connection occurs within the allowed duration.
+ private static final String ADB_TEMP_KEYS_FILE = "adb_temp_keys.xml";
+ private static final String XML_TAG_ADB_KEY = "adbKey";
+ private static final String XML_ATTRIBUTE_KEY = "key";
+ private static final String XML_ATTRIBUTE_LAST_CONNECTION = "lastConnection";
+
+ /**
+ * Value returned by {@code getLastConnectionTime} when there is no previously saved
+ * connection time for the specified key.
+ */
+ public static final long NO_PREVIOUS_CONNECTION = 0;
+
+ /**
+ * Constructor that uses the default location for the persistent adb key store.
+ */
+ AdbKeyStore() {
+ initKeyFile();
+ mKeyMap = getKeyMapFromFile();
+ }
+
+ /**
+ * Constructor that uses the specified file as the location for the persistent adb key
+ * store.
+ */
+ AdbKeyStore(File keyFile) {
+ mKeyFile = keyFile;
+ initKeyFile();
+ mKeyMap = getKeyMapFromFile();
+ }
+
+ /**
+ * Initializes the key file that will be used to persist the adb grants.
+ */
+ private void initKeyFile() {
+ if (mKeyFile == null) {
+ mKeyFile = getAdbFile(ADB_TEMP_KEYS_FILE);
+ }
+ // getAdbFile can return null if the adb file cannot be obtained
+ if (mKeyFile != null) {
+ mAtomicKeyFile = new AtomicFile(mKeyFile);
+ }
+ }
+
+ /**
+ * Returns the key map with the keys and last connection times from the key file.
+ */
+ private Map<String, Long> getKeyMapFromFile() {
+ Map<String, Long> keyMap = new HashMap<String, Long>();
+ // if the AtomicFile could not be instantiated before attempt again; if it still fails
+ // return an empty key map.
+ if (mAtomicKeyFile == null) {
+ initKeyFile();
+ if (mAtomicKeyFile == null) {
+ Slog.e(TAG, "Unable to obtain the key file, " + mKeyFile + ", for reading");
+ return keyMap;
+ }
+ }
+ if (!mAtomicKeyFile.exists()) {
+ return keyMap;
+ }
+ try (FileInputStream keyStream = mAtomicKeyFile.openRead()) {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(keyStream, StandardCharsets.UTF_8.name());
+ XmlUtils.beginDocument(parser, XML_TAG_ADB_KEY);
+ while (parser.next() != XmlPullParser.END_DOCUMENT) {
+ String tagName = parser.getName();
+ if (tagName == null) {
+ break;
+ } else if (!tagName.equals(XML_TAG_ADB_KEY)) {
+ XmlUtils.skipCurrentTag(parser);
+ continue;
+ }
+ String key = parser.getAttributeValue(null, XML_ATTRIBUTE_KEY);
+ long connectionTime;
+ try {
+ connectionTime = Long.valueOf(
+ parser.getAttributeValue(null, XML_ATTRIBUTE_LAST_CONNECTION));
+ } catch (NumberFormatException e) {
+ Slog.e(TAG,
+ "Caught a NumberFormatException parsing the last connection time: "
+ + e);
+ XmlUtils.skipCurrentTag(parser);
+ continue;
+ }
+ keyMap.put(key, connectionTime);
+ }
+ } catch (IOException | XmlPullParserException e) {
+ Slog.e(TAG, "Caught an exception parsing the XML key file: ", e);
+ }
+ return keyMap;
+ }
+
+ /**
+ * Writes the key map to the key file.
+ */
+ public void persistKeyStore() {
+ // if there is nothing in the key map then ensure any keys left in the key store files
+ // are deleted as well.
+ if (mKeyMap.size() == 0) {
+ deleteKeyStore();
+ return;
+ }
+ if (mAtomicKeyFile == null) {
+ initKeyFile();
+ if (mAtomicKeyFile == null) {
+ Slog.e(TAG, "Unable to obtain the key file, " + mKeyFile + ", for writing");
+ return;
+ }
+ }
+ FileOutputStream keyStream = null;
+ try {
+ XmlSerializer serializer = new FastXmlSerializer();
+ keyStream = mAtomicKeyFile.startWrite();
+ serializer.setOutput(keyStream, StandardCharsets.UTF_8.name());
+ serializer.startDocument(null, true);
+ long allowedTime = getAllowedConnectionTime();
+ long systemTime = System.currentTimeMillis();
+ Iterator keyMapIterator = mKeyMap.entrySet().iterator();
+ while (keyMapIterator.hasNext()) {
+ Map.Entry<String, Long> keyEntry = (Map.Entry) keyMapIterator.next();
+ long connectionTime = keyEntry.getValue();
+ if (systemTime < (connectionTime + allowedTime)) {
+ serializer.startTag(null, XML_TAG_ADB_KEY);
+ serializer.attribute(null, XML_ATTRIBUTE_KEY, keyEntry.getKey());
+ serializer.attribute(null, XML_ATTRIBUTE_LAST_CONNECTION,
+ String.valueOf(keyEntry.getValue()));
+ serializer.endTag(null, XML_TAG_ADB_KEY);
+ } else {
+ keyMapIterator.remove();
+ }
+ }
+ serializer.endDocument();
+ mAtomicKeyFile.finishWrite(keyStream);
+ } catch (IOException e) {
+ Slog.e(TAG, "Caught an exception writing the key map: ", e);
+ mAtomicKeyFile.failWrite(keyStream);
+ }
+ }
+
+ /**
+ * Removes all of the entries in the key map and deletes the key file.
+ */
+ public void deleteKeyStore() {
+ mKeyMap.clear();
+ if (mAtomicKeyFile == null) {
+ return;
+ }
+ mAtomicKeyFile.delete();
+ }
+
+ /**
+ * Returns the time of the last connection from the specified key, or {@code
+ * NO_PREVIOUS_CONNECTION} if the specified key does not have an active adb grant.
+ */
+ public long getLastConnectionTime(String key) {
+ return mKeyMap.getOrDefault(key, NO_PREVIOUS_CONNECTION);
+ }
+
+ /**
+ * Sets the time of the last connection for the specified key to the provided time.
+ */
+ public void setLastConnectionTime(String key, long connectionTime) {
+ // Do not set the connection time to a value that is earlier than what was previously
+ // stored as the last connection time.
+ if (mKeyMap.containsKey(key) && mKeyMap.get(key) >= connectionTime) {
+ return;
+ }
+ mKeyMap.put(key, connectionTime);
+ sendPersistKeyStoreMessage();
+ }
+
+ /**
+ * Returns whether the specified key should be authroized to connect without user
+ * interaction. This requires that the user previously connected this device and selected
+ * the option to 'Always allow', and the time since the last connection is within the
+ * allowed window.
+ */
+ public boolean isKeyAuthorized(String key) {
+ long lastConnectionTime = getLastConnectionTime(key);
+ if (lastConnectionTime == NO_PREVIOUS_CONNECTION) {
+ return false;
+ }
+ long allowedConnectionTime = getAllowedConnectionTime();
+ // if the allowed connection time is 0 then revert to the previous behavior of always
+ // allowing previously granted adb grants.
+ if (allowedConnectionTime == 0 || (System.currentTimeMillis() < (lastConnectionTime
+ + allowedConnectionTime))) {
+ return true;
+ } else {
+ // since this key is no longer auhorized remove it from the Map
+ removeKey(key);
+ return false;
+ }
+ }
+
+ /**
+ * Returns the connection time within which a connection from an allowed key is
+ * automatically allowed without user interaction.
+ */
+ public long getAllowedConnectionTime() {
+ return Settings.Global.getLong(mContext.getContentResolver(),
+ Settings.Global.ADB_ALLOWED_CONNECTION_TIME,
+ Settings.Global.DEFAULT_ADB_ALLOWED_CONNECTION_TIME);
+ }
+
+ /**
+ * Removes the specified key from the key store.
+ */
+ public void removeKey(String key) {
+ if (!mKeyMap.containsKey(key)) {
+ return;
+ }
+ mKeyMap.remove(key);
+ sendPersistKeyStoreMessage();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 089847d..b24290f 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -64,6 +64,7 @@
import static android.os.Process.SIGNAL_USR1;
import static android.os.Process.SYSTEM_UID;
import static android.os.Process.THREAD_PRIORITY_FOREGROUND;
+import static android.os.Process.ZYGOTE_PROCESS;
import static android.os.Process.getTotalMemory;
import static android.os.Process.isThreadInProcess;
import static android.os.Process.killProcess;
@@ -75,7 +76,6 @@
import static android.os.Process.sendSignal;
import static android.os.Process.setThreadPriority;
import static android.os.Process.setThreadScheduler;
-import static android.os.Process.zygoteProcess;
import static android.provider.Settings.Global.ALWAYS_FINISH_ACTIVITIES;
import static android.provider.Settings.Global.DEBUG_APP;
import static android.provider.Settings.Global.NETWORK_ACCESS_TIMEOUT_MS;
@@ -2162,7 +2162,7 @@
? Collections.emptyList()
: Arrays.asList(exemptions.split(","));
}
- if (!zygoteProcess.setApiBlacklistExemptions(mExemptions)) {
+ if (!ZYGOTE_PROCESS.setApiBlacklistExemptions(mExemptions)) {
Slog.e(TAG, "Failed to set API blacklist exemptions!");
// leave mExemptionsStr as is, so we don't try to send the same list again.
mExemptions = Collections.emptyList();
@@ -2175,7 +2175,7 @@
}
if (logSampleRate != -1 && logSampleRate != mLogSampleRate) {
mLogSampleRate = logSampleRate;
- zygoteProcess.setHiddenApiAccessLogSampleRate(mLogSampleRate);
+ ZYGOTE_PROCESS.setHiddenApiAccessLogSampleRate(mLogSampleRate);
}
mPolicy = getValidEnforcementPolicy(Settings.Global.HIDDEN_API_POLICY);
}
@@ -2864,16 +2864,18 @@
* @param userId
* @param event
* @param appToken ActivityRecord's appToken.
+ * @param taskRoot TaskRecord's root
*/
public void updateActivityUsageStats(ComponentName activity, int userId, int event,
- IBinder appToken) {
+ IBinder appToken, ComponentName taskRoot) {
if (DEBUG_SWITCH) {
Slog.d(TAG_SWITCH, "updateActivityUsageStats: comp="
+ activity + " hash=" + appToken.hashCode() + " event=" + event);
}
synchronized (this) {
if (mUsageStatsService != null) {
- mUsageStatsService.reportEvent(activity, userId, event, appToken.hashCode());
+ mUsageStatsService.reportEvent(activity, userId, event, appToken.hashCode(),
+ taskRoot);
}
}
}
@@ -2911,7 +2913,7 @@
if (mUsageStatsService != null) {
mUsageStatsService.reportEvent(service, userId,
started ? UsageEvents.Event.FOREGROUND_SERVICE_START
- : UsageEvents.Event.FOREGROUND_SERVICE_STOP, 0);
+ : UsageEvents.Event.FOREGROUND_SERVICE_STOP, 0, null);
}
}
}
@@ -4880,7 +4882,7 @@
ArraySet<String> completedIsas = new ArraySet<String>();
for (String abi : Build.SUPPORTED_ABIS) {
- zygoteProcess.establishZygoteConnectionForAbi(abi);
+ ZYGOTE_PROCESS.establishZygoteConnectionForAbi(abi);
final String instructionSet = VMRuntime.getInstructionSet(abi);
if (!completedIsas.contains(instructionSet)) {
try {
@@ -17521,10 +17523,10 @@
@Override
public void updateActivityUsageStats(ComponentName activity, int userId, int event,
- IBinder appToken) {
+ IBinder appToken, ComponentName taskRoot) {
synchronized (ActivityManagerService.this) {
ActivityManagerService.this.updateActivityUsageStats(activity, userId, event,
- appToken);
+ appToken, taskRoot);
}
}
diff --git a/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java b/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
index ac74598..79b56c6 100644
--- a/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
+++ b/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
@@ -20,7 +20,6 @@
import android.net.ConnectivityMetricsEvent;
import android.net.IIpConnectivityMetrics;
import android.net.INetdEventCallback;
-import android.net.ip.IpClient;
import android.net.metrics.ApfProgramEvent;
import android.net.metrics.IpConnectivityLog;
import android.os.Binder;
@@ -270,8 +269,6 @@
// Dump the rolling buffer of metrics event and pretty print events using a human readable
// format. Also print network dns/connect statistics and default network event time series.
static final String CMD_LIST = "list";
- // Dump all IpClient logs ("ipclient").
- static final String CMD_IPCLIENT = IpClient.DUMP_ARG;
// By default any other argument will fall into the default case which is the equivalent
// of calling both the "list" and "ipclient" commands. This includes most notably bug
// reports collected by dumpsys.cpp with the "-a" argument.
@@ -295,20 +292,9 @@
case CMD_PROTO:
cmdListAsProto(pw);
return;
- case CMD_IPCLIENT: {
- final String[] ipclientArgs = ((args != null) && (args.length > 1))
- ? Arrays.copyOfRange(args, 1, args.length)
- : null;
- IpClient.dumpAllLogs(pw, ipclientArgs);
- return;
- }
case CMD_LIST:
- cmdList(pw);
- return;
default:
cmdList(pw);
- pw.println("");
- IpClient.dumpAllLogs(pw, null);
return;
}
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index eca371a..3fd3945 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -4106,25 +4106,6 @@
return mCurrentSubtype;
}
- @Override
- public boolean setCurrentInputMethodSubtype(InputMethodSubtype subtype) {
- synchronized (mMethodMap) {
- // TODO: Make this work even for non-current users?
- if (!calledFromValidUserLocked()) {
- return false;
- }
- if (subtype != null && mCurMethodId != null) {
- InputMethodInfo imi = mMethodMap.get(mCurMethodId);
- int subtypeId = InputMethodUtils.getSubtypeIdFromHashCode(imi, subtype.hashCode());
- if (subtypeId != NOT_A_SUBTYPE_ID) {
- setInputMethodLocked(mCurMethodId, subtypeId);
- return true;
- }
- }
- return false;
- }
- }
-
private List<InputMethodInfo> getInputMethodListAsUser(@UserIdInt int userId) {
synchronized (mMethodMap) {
return getInputMethodListLocked(userId);
diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
index a31b3b4..3222143 100644
--- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
@@ -1519,13 +1519,6 @@
@BinderThread
@Override
- public boolean setCurrentInputMethodSubtype(InputMethodSubtype subtype) {
- reportNotSupported();
- return false;
- }
-
- @BinderThread
- @Override
public void setAdditionalInputMethodSubtypes(String imiId, InputMethodSubtype[] subtypes) {
reportNotSupported();
}
diff --git a/services/core/java/com/android/server/os/BugreportManagerService.java b/services/core/java/com/android/server/os/BugreportManagerService.java
index e241591..bee7a8b 100644
--- a/services/core/java/com/android/server/os/BugreportManagerService.java
+++ b/services/core/java/com/android/server/os/BugreportManagerService.java
@@ -37,7 +37,6 @@
@Override
public void onStart() {
mService = new BugreportManagerServiceImpl(getContext());
- // TODO(b/111441001): Needs sepolicy to be submitted first.
- // publishBinderService(Context.BUGREPORT_SERVICE, mService);
+ publishBinderService(Context.BUGREPORT_SERVICE, mService);
}
}
diff --git a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
index 1178cc1..f736056 100644
--- a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
+++ b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
@@ -44,6 +44,7 @@
*/
class BugreportManagerServiceImpl extends IDumpstate.Stub {
private static final String TAG = "BugreportManagerService";
+ private static final String BUGREPORT_SERVICE = "bugreportd";
private static final long DEFAULT_BUGREPORT_SERVICE_TIMEOUT_MILLIS = 30 * 1000;
private IDumpstate mDs = null;
@@ -64,6 +65,8 @@
throw new UnsupportedOperationException("setListener is not allowed on this service");
}
+ // TODO(b/111441001): Intercept onFinished here in system server and shutdown
+ // the bugreportd service.
@Override
@RequiresPermission(android.Manifest.permission.DUMP)
public void startBugreport(int callingUidUnused, String callingPackage,
@@ -84,6 +87,14 @@
bugreportFd, screenshotFd, bugreportMode, listener);
}
+ @Override
+ @RequiresPermission(android.Manifest.permission.DUMP)
+ public void cancelBugreport() throws RemoteException {
+ // This tells init to cancel bugreportd service.
+ SystemProperties.set("ctl.stop", BUGREPORT_SERVICE);
+ mDs = null;
+ }
+
private boolean validate(@BugreportParams.BugreportMode int mode) {
if (mode != BugreportParams.BUGREPORT_MODE_FULL
&& mode != BugreportParams.BUGREPORT_MODE_INTERACTIVE
@@ -107,7 +118,7 @@
*/
private IDumpstate getDumpstateService() {
// Start bugreport service.
- SystemProperties.set("ctl.start", "bugreport");
+ SystemProperties.set("ctl.start", BUGREPORT_SERVICE);
IDumpstate ds = null;
boolean timedOut = false;
diff --git a/services/core/java/com/android/server/pm/OWNERS b/services/core/java/com/android/server/pm/OWNERS
new file mode 100644
index 0000000..60d7925
--- /dev/null
+++ b/services/core/java/com/android/server/pm/OWNERS
@@ -0,0 +1,76 @@
+hackbod@android.com
+hackbod@google.com
+jsharkey@android.com
+jsharkey@google.com
+narayan@google.com
+patb@google.com
+svetoslavganov@android.com
+svetoslavganov@google.com
+toddke@android.com
+toddke@google.com
+
+# dex
+per-file AbstractStatsBase.java = agampe@google.com
+per-file AbstractStatsBase.java = calin@google.com
+per-file AbstractStatsBase.java = ngeoffray@google.com
+per-file BackgroundDexOptService.java = agampe@google.com
+per-file BackgroundDexOptService.java = calin@google.com
+per-file BackgroundDexOptService.java = ngeoffray@google.com
+per-file CompilerStats.java = agampe@google.com
+per-file CompilerStats.java = calin@google.com
+per-file CompilerStats.java = ngeoffray@google.com
+per-file InstructionSets.java = agampe@google.com
+per-file InstructionSets.java = calin@google.com
+per-file InstructionSets.java = ngeoffray@google.com
+per-file OtaDexoptService.java = agampe@google.com
+per-file OtaDexoptService.java = calin@google.com
+per-file OtaDexoptService.java = ngeoffray@google.com
+per-file OtaDexoptShellCommand.java = agampe@google.com
+per-file OtaDexoptShellCommand.java = calin@google.com
+per-file OtaDexoptShellCommand.java = ngeoffray@google.com
+per-file PackageDexOptimizer.java = agampe@google.com
+per-file PackageDexOptimizer.java = calin@google.com
+per-file PackageDexOptimizer.java = ngeoffray@google.com
+per-file PackageManagerServiceCompilerMapping.java = agampe@google.com
+per-file PackageManagerServiceCompilerMapping.java = calin@google.com
+per-file PackageManagerServiceCompilerMapping.java = ngeoffray@google.com
+per-file PackageUsage.java = agampe@google.com
+per-file PackageUsage.java = calin@google.com
+per-file PackageUsage.java = ngeoffray@google.com
+
+# multi user / cross profile
+per-file CrossProfileAppsServiceImpl.java = omakoto@google.com
+per-file CrossProfileAppsServiceImpl.java = yamasani@google.com
+per-file CrossProfileAppsService.java = omakoto@google.com
+per-file CrossProfileAppsService.java = yamasani@google.com
+per-file CrossProfileIntentFilter.java = omakoto@google.com
+per-file CrossProfileIntentFilter.java = yamasani@google.com
+per-file CrossProfileIntentResolver.java = omakoto@google.com
+per-file CrossProfileIntentResolver.java = yamasani@google.com
+per-file UserManagerService.java = omakoto@google.com
+per-file UserManagerService.java = yamasani@google.com
+per-file UserRestrictionsUtils.java = omakoto@google.com
+per-file UserRestrictionsUtils.java = yamasani@google.com
+
+# security
+per-file KeySetHandle.java = cbrubaker@google.com
+per-file KeySetManagerService.java = cbrubaker@google.com
+per-file PackageKeySetData.java = cbrubaker@google.com
+per-file PackageSignatures.java = cbrubaker@google.com
+per-file SELinuxMMAC.java = cbrubaker@google.com
+
+# shortcuts
+per-file LauncherAppsService.java = omakoto@google.com
+per-file ShareTargetInfo.java = omakoto@google.com
+per-file ShortcutBitmapSaver.java = omakoto@google.com
+per-file ShortcutDumpFiles.java = omakoto@google.com
+per-file ShortcutLauncher.java = omakoto@google.com
+per-file ShortcutNonPersistentUser.java = omakoto@google.com
+per-file ShortcutPackage.java = omakoto@google.com
+per-file ShortcutPackageInfo.java = omakoto@google.com
+per-file ShortcutPackageItem.java = omakoto@google.com
+per-file ShortcutParser.java = omakoto@google.com
+per-file ShortcutRequestPinProcessor.java = omakoto@google.com
+per-file ShortcutService.java = omakoto@google.com
+per-file ShortcutUser.java = omakoto@google.com
+
diff --git a/services/core/java/com/android/server/pm/dex/OWNERS b/services/core/java/com/android/server/pm/dex/OWNERS
new file mode 100644
index 0000000..fcc1f6c
--- /dev/null
+++ b/services/core/java/com/android/server/pm/dex/OWNERS
@@ -0,0 +1,4 @@
+agampe@google.com
+calin@google.com
+ngeoffray@google.com
+sehr@google.com
diff --git a/services/core/java/com/android/server/pm/permission/OWNERS b/services/core/java/com/android/server/pm/permission/OWNERS
index 88b97ea..01dc01e 100644
--- a/services/core/java/com/android/server/pm/permission/OWNERS
+++ b/services/core/java/com/android/server/pm/permission/OWNERS
@@ -1,4 +1,4 @@
-per-file DefaultPermissionGrantPolicy.java = bpoiesz@google.com
+moltmann@google.com
per-file DefaultPermissionGrantPolicy.java = hackbod@android.com
per-file DefaultPermissionGrantPolicy.java = jsharkey@android.com
per-file DefaultPermissionGrantPolicy.java = svetoslavganov@google.com
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 61c4863..2affa97 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -5313,9 +5313,18 @@
}
void updateActivityUsageStats(ActivityRecord activity, int event) {
+ ComponentName taskRoot = null;
+ final TaskRecord task = activity.getTaskRecord();
+ if (task != null) {
+ final ActivityRecord rootActivity = task.getRootActivity();
+ if (rootActivity != null) {
+ taskRoot = rootActivity.mActivityComponent;
+ }
+ }
+
final Message m = PooledLambda.obtainMessage(
ActivityManagerInternal::updateActivityUsageStats, mAmInternal,
- activity.mActivityComponent, activity.mUserId, event, activity.appToken);
+ activity.mActivityComponent, activity.mUserId, event, activity.appToken, taskRoot);
mH.sendMessage(m);
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index fb7e47d..54053a8 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -7667,7 +7667,18 @@
}
// Shutting down backup manager service permanently.
- toggleBackupServiceActive(UserHandle.USER_SYSTEM, /* makeActive= */ false);
+ long ident = mInjector.binderClearCallingIdentity();
+ try {
+ if (mInjector.getIBackupManager() != null) {
+ mInjector.getIBackupManager()
+ .setBackupServiceActive(UserHandle.USER_SYSTEM, false);
+ }
+ } catch (RemoteException e) {
+ throw new IllegalStateException("Failed deactivating backup service.", e);
+ } finally {
+ mInjector.binderRestoreCallingIdentity(ident);
+ }
+
if (isAdb()) {
// Log device owner provisioning was started using adb.
MetricsLogger.action(mContext, PROVISIONING_ENTRY_POINT_ADB, LOG_TAG_DEVICE_OWNER);
@@ -7695,7 +7706,7 @@
saveUserRestrictionsLocked(userId);
}
- long ident = mInjector.binderClearCallingIdentity();
+ ident = mInjector.binderClearCallingIdentity();
try {
// TODO Send to system too?
sendOwnerChangedBroadcast(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED, userId);
@@ -7952,9 +7963,6 @@
.write();
}
- // Shutting down backup manager service permanently.
- toggleBackupServiceActive(userHandle, /* makeActive= */ false);
-
mOwners.setProfileOwner(who, ownerName, userHandle);
mOwners.writeProfileOwner(userHandle);
Slog.i(LOG_TAG, "Profile owner set: " + who + " on user " + userHandle);
@@ -7978,24 +7986,6 @@
}
}
-
- private void toggleBackupServiceActive(int userId, boolean makeActive) {
- // Shutting down backup manager service permanently.
- enforceUserUnlocked(userId);
- long ident = mInjector.binderClearCallingIdentity();
- try {
- if (mInjector.getIBackupManager() != null) {
- mInjector.getIBackupManager()
- .setBackupServiceActive(userId, makeActive);
- }
- } catch (RemoteException e) {
- throw new IllegalStateException("Failed deactivating backup service.", e);
- } finally {
- mInjector.binderRestoreCallingIdentity(ident);
- }
-
- }
-
@Override
public void clearProfileOwner(ComponentName who) {
if (!mHasFeature) {
@@ -12748,9 +12738,22 @@
return;
}
Preconditions.checkNotNull(admin);
- enforceProfileOrDeviceOwner(admin);
- int userId = mInjector.userHandleGetCallingUserId();
- toggleBackupServiceActive(userId, enabled);
+ synchronized (getLockObject()) {
+ getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ }
+
+ final long ident = mInjector.binderClearCallingIdentity();
+ try {
+ IBackupManager ibm = mInjector.getIBackupManager();
+ if (ibm != null) {
+ ibm.setBackupServiceActive(UserHandle.USER_SYSTEM, enabled);
+ }
+ } catch (RemoteException e) {
+ throw new IllegalStateException(
+ "Failed " + (enabled ? "" : "de") + "activating backup service.", e);
+ } finally {
+ mInjector.binderRestoreCallingIdentity(ident);
+ }
}
@Override
@@ -12759,13 +12762,11 @@
if (!mHasFeature) {
return true;
}
-
- enforceProfileOrDeviceOwner(admin);
synchronized (getLockObject()) {
try {
+ getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
IBackupManager ibm = mInjector.getIBackupManager();
- return ibm != null && ibm.isBackupServiceActive(
- mInjector.userHandleGetCallingUserId());
+ return ibm != null && ibm.isBackupServiceActive(UserHandle.USER_SYSTEM);
} catch (RemoteException e) {
throw new IllegalStateException("Failed requesting backup service state.", e);
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 623990b..65eaf554 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -873,7 +873,7 @@
TimingsTraceLog traceLog = new TimingsTraceLog(
SYSTEM_SERVER_TIMING_ASYNC_TAG, Trace.TRACE_TAG_SYSTEM_SERVER);
traceLog.traceBegin(SECONDARY_ZYGOTE_PRELOAD);
- if (!Process.zygoteProcess.preloadDefault(Build.SUPPORTED_32_BIT_ABIS[0])) {
+ if (!Process.ZYGOTE_PROCESS.preloadDefault(Build.SUPPORTED_32_BIT_ABIS[0])) {
Slog.e(TAG, "Unable to preload default resources");
}
traceLog.traceEnd();
diff --git a/services/net/Android.bp b/services/net/Android.bp
index 3b4d6a7..30c7de5 100644
--- a/services/net/Android.bp
+++ b/services/net/Android.bp
@@ -3,18 +3,18 @@
srcs: ["java/**/*.java"],
}
-// TODO: move to networking module with DhcpClient and remove lib
-java_library {
- name: "dhcp-packet-lib",
- srcs: [
- "java/android/net/dhcp/*Packet.java",
- ]
-}
-
filegroup {
name: "services-networkstack-shared-srcs",
srcs: [
- "java/android/net/util/FdEventsReader.java", // TODO: move to NetworkStack with IpClient
+ "java/android/net/ip/InterfaceController.java", // TODO: move to NetworkStack with tethering
+ "java/android/net/util/InterfaceParams.java", // TODO: move to NetworkStack with IpServer
"java/android/net/shared/*.java",
+ ],
+}
+
+java_library {
+ name: "services-netlink-lib",
+ srcs: [
+ "java/android/net/netlink/*.java",
]
}
diff --git a/services/net/java/android/net/dhcp/DhcpClient.java b/services/net/java/android/net/dhcp/DhcpClient.java
index 04ac9a3..cddb91f 100644
--- a/services/net/java/android/net/dhcp/DhcpClient.java
+++ b/services/net/java/android/net/dhcp/DhcpClient.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,1037 +16,14 @@
package android.net.dhcp;
-import static android.net.dhcp.DhcpPacket.DHCP_BROADCAST_ADDRESS;
-import static android.net.dhcp.DhcpPacket.DHCP_DNS_SERVER;
-import static android.net.dhcp.DhcpPacket.DHCP_DOMAIN_NAME;
-import static android.net.dhcp.DhcpPacket.DHCP_LEASE_TIME;
-import static android.net.dhcp.DhcpPacket.DHCP_MTU;
-import static android.net.dhcp.DhcpPacket.DHCP_REBINDING_TIME;
-import static android.net.dhcp.DhcpPacket.DHCP_RENEWAL_TIME;
-import static android.net.dhcp.DhcpPacket.DHCP_ROUTER;
-import static android.net.dhcp.DhcpPacket.DHCP_SUBNET_MASK;
-import static android.net.dhcp.DhcpPacket.DHCP_VENDOR_INFO;
-import static android.net.dhcp.DhcpPacket.INADDR_ANY;
-import static android.net.dhcp.DhcpPacket.INADDR_BROADCAST;
-import static android.net.util.SocketUtils.makePacketSocketAddress;
-import static android.system.OsConstants.AF_INET;
-import static android.system.OsConstants.AF_PACKET;
-import static android.system.OsConstants.ETH_P_IP;
-import static android.system.OsConstants.IPPROTO_UDP;
-import static android.system.OsConstants.SOCK_DGRAM;
-import static android.system.OsConstants.SOCK_RAW;
-import static android.system.OsConstants.SOL_SOCKET;
-import static android.system.OsConstants.SO_BROADCAST;
-import static android.system.OsConstants.SO_RCVBUF;
-import static android.system.OsConstants.SO_REUSEADDR;
-
-import android.content.Context;
-import android.net.DhcpResults;
-import android.net.NetworkUtils;
-import android.net.TrafficStats;
-import android.net.ip.IpClient;
-import android.net.metrics.DhcpClientEvent;
-import android.net.metrics.DhcpErrorEvent;
-import android.net.metrics.IpConnectivityLog;
-import android.net.util.InterfaceParams;
-import android.net.util.SocketUtils;
-import android.os.Message;
-import android.os.SystemClock;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.util.EventLog;
-import android.util.Log;
-import android.util.SparseArray;
-
-import com.android.internal.util.HexDump;
-import com.android.internal.util.MessageUtils;
-import com.android.internal.util.State;
-import com.android.internal.util.StateMachine;
-import com.android.internal.util.WakeupMessage;
-
-import libcore.io.IoBridge;
-
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.net.Inet4Address;
-import java.net.SocketAddress;
-import java.net.SocketException;
-import java.nio.ByteBuffer;
-import java.util.Arrays;
-import java.util.Random;
-
/**
- * A DHCPv4 client.
- *
- * Written to behave similarly to the DhcpStateMachine + dhcpcd 5.5.6 combination used in Android
- * 5.1 and below, as configured on Nexus 6. The interface is the same as DhcpStateMachine.
- *
- * TODO:
- *
- * - Exponential backoff when receiving NAKs (not specified by the RFC, but current behaviour).
- * - Support persisting lease state and support INIT-REBOOT. Android 5.1 does this, but it does not
- * do so correctly: instead of requesting the lease last obtained on a particular network (e.g., a
- * given SSID), it requests the last-leased IP address on the same interface, causing a delay if
- * the server NAKs or a timeout if it doesn't.
- *
- * Known differences from current behaviour:
- *
- * - Does not request the "static routes" option.
- * - Does not support BOOTP servers. DHCP has been around since 1993, should be everywhere now.
- * - Requests the "broadcast" option, but does nothing with it.
- * - Rejects invalid subnet masks such as 255.255.255.1 (current code treats that as 255.255.255.0).
- *
- * @hide
+ * TODO: remove this class after migrating clients.
*/
-public class DhcpClient extends StateMachine {
+public class DhcpClient {
+ public static final int CMD_PRE_DHCP_ACTION = 1003;
+ public static final int CMD_POST_DHCP_ACTION = 1004;
+ public static final int CMD_PRE_DHCP_ACTION_COMPLETE = 1006;
- private static final String TAG = "DhcpClient";
- private static final boolean DBG = true;
- private static final boolean STATE_DBG = false;
- private static final boolean MSG_DBG = false;
- private static final boolean PACKET_DBG = false;
-
- // Timers and timeouts.
- private static final int SECONDS = 1000;
- private static final int FIRST_TIMEOUT_MS = 2 * SECONDS;
- private static final int MAX_TIMEOUT_MS = 128 * SECONDS;
-
- // This is not strictly needed, since the client is asynchronous and implements exponential
- // backoff. It's maintained for backwards compatibility with the previous DHCP code, which was
- // a blocking operation with a 30-second timeout. We pick 36 seconds so we can send packets at
- // t=0, t=2, t=6, t=14, t=30, allowing for 10% jitter.
- private static final int DHCP_TIMEOUT_MS = 36 * SECONDS;
-
- // DhcpClient uses IpClient's handler.
- private static final int PUBLIC_BASE = IpClient.DHCPCLIENT_CMD_BASE;
-
- /* Commands from controller to start/stop DHCP */
- public static final int CMD_START_DHCP = PUBLIC_BASE + 1;
- public static final int CMD_STOP_DHCP = PUBLIC_BASE + 2;
-
- /* Notification from DHCP state machine prior to DHCP discovery/renewal */
- public static final int CMD_PRE_DHCP_ACTION = PUBLIC_BASE + 3;
- /* Notification from DHCP state machine post DHCP discovery/renewal. Indicates
- * success/failure */
- public static final int CMD_POST_DHCP_ACTION = PUBLIC_BASE + 4;
- /* Notification from DHCP state machine before quitting */
- public static final int CMD_ON_QUIT = PUBLIC_BASE + 5;
-
- /* Command from controller to indicate DHCP discovery/renewal can continue
- * after pre DHCP action is complete */
- public static final int CMD_PRE_DHCP_ACTION_COMPLETE = PUBLIC_BASE + 6;
-
- /* Command and event notification to/from IpManager requesting the setting
- * (or clearing) of an IPv4 LinkAddress.
- */
- public static final int CMD_CLEAR_LINKADDRESS = PUBLIC_BASE + 7;
- public static final int CMD_CONFIGURE_LINKADDRESS = PUBLIC_BASE + 8;
- public static final int EVENT_LINKADDRESS_CONFIGURED = PUBLIC_BASE + 9;
-
- /* Message.arg1 arguments to CMD_POST_DHCP_ACTION notification */
public static final int DHCP_SUCCESS = 1;
public static final int DHCP_FAILURE = 2;
-
- // Internal messages.
- private static final int PRIVATE_BASE = IpClient.DHCPCLIENT_CMD_BASE + 100;
- private static final int CMD_KICK = PRIVATE_BASE + 1;
- private static final int CMD_RECEIVED_PACKET = PRIVATE_BASE + 2;
- private static final int CMD_TIMEOUT = PRIVATE_BASE + 3;
- private static final int CMD_RENEW_DHCP = PRIVATE_BASE + 4;
- private static final int CMD_REBIND_DHCP = PRIVATE_BASE + 5;
- private static final int CMD_EXPIRE_DHCP = PRIVATE_BASE + 6;
-
- // For message logging.
- private static final Class[] sMessageClasses = { DhcpClient.class };
- private static final SparseArray<String> sMessageNames =
- MessageUtils.findMessageNames(sMessageClasses);
-
- // DHCP parameters that we request.
- /* package */ static final byte[] REQUESTED_PARAMS = new byte[] {
- DHCP_SUBNET_MASK,
- DHCP_ROUTER,
- DHCP_DNS_SERVER,
- DHCP_DOMAIN_NAME,
- DHCP_MTU,
- DHCP_BROADCAST_ADDRESS, // TODO: currently ignored.
- DHCP_LEASE_TIME,
- DHCP_RENEWAL_TIME,
- DHCP_REBINDING_TIME,
- DHCP_VENDOR_INFO,
- };
-
- // DHCP flag that means "yes, we support unicast."
- private static final boolean DO_UNICAST = false;
-
- // System services / libraries we use.
- private final Context mContext;
- private final Random mRandom;
- private final IpConnectivityLog mMetricsLog = new IpConnectivityLog();
-
- // Sockets.
- // - We use a packet socket to receive, because servers send us packets bound for IP addresses
- // which we have not yet configured, and the kernel protocol stack drops these.
- // - We use a UDP socket to send, so the kernel handles ARP and routing for us (DHCP servers can
- // be off-link as well as on-link).
- private FileDescriptor mPacketSock;
- private FileDescriptor mUdpSock;
- private ReceiveThread mReceiveThread;
-
- // State variables.
- private final StateMachine mController;
- private final WakeupMessage mKickAlarm;
- private final WakeupMessage mTimeoutAlarm;
- private final WakeupMessage mRenewAlarm;
- private final WakeupMessage mRebindAlarm;
- private final WakeupMessage mExpiryAlarm;
- private final String mIfaceName;
-
- private boolean mRegisteredForPreDhcpNotification;
- private InterfaceParams mIface;
- // TODO: MacAddress-ify more of this class hierarchy.
- private byte[] mHwAddr;
- private SocketAddress mInterfaceBroadcastAddr;
- private int mTransactionId;
- private long mTransactionStartMillis;
- private DhcpResults mDhcpLease;
- private long mDhcpLeaseExpiry;
- private DhcpResults mOffer;
-
- // Milliseconds SystemClock timestamps used to record transition times to DhcpBoundState.
- private long mLastInitEnterTime;
- private long mLastBoundExitTime;
-
- // States.
- private State mStoppedState = new StoppedState();
- private State mDhcpState = new DhcpState();
- private State mDhcpInitState = new DhcpInitState();
- private State mDhcpSelectingState = new DhcpSelectingState();
- private State mDhcpRequestingState = new DhcpRequestingState();
- private State mDhcpHaveLeaseState = new DhcpHaveLeaseState();
- private State mConfiguringInterfaceState = new ConfiguringInterfaceState();
- private State mDhcpBoundState = new DhcpBoundState();
- private State mDhcpRenewingState = new DhcpRenewingState();
- private State mDhcpRebindingState = new DhcpRebindingState();
- private State mDhcpInitRebootState = new DhcpInitRebootState();
- private State mDhcpRebootingState = new DhcpRebootingState();
- private State mWaitBeforeStartState = new WaitBeforeStartState(mDhcpInitState);
- private State mWaitBeforeRenewalState = new WaitBeforeRenewalState(mDhcpRenewingState);
-
- private WakeupMessage makeWakeupMessage(String cmdName, int cmd) {
- cmdName = DhcpClient.class.getSimpleName() + "." + mIfaceName + "." + cmdName;
- return new WakeupMessage(mContext, getHandler(), cmdName, cmd);
- }
-
- // TODO: Take an InterfaceParams instance instead of an interface name String.
- private DhcpClient(Context context, StateMachine controller, String iface) {
- super(TAG, controller.getHandler());
-
- mContext = context;
- mController = controller;
- mIfaceName = iface;
-
- addState(mStoppedState);
- addState(mDhcpState);
- addState(mDhcpInitState, mDhcpState);
- addState(mWaitBeforeStartState, mDhcpState);
- addState(mDhcpSelectingState, mDhcpState);
- addState(mDhcpRequestingState, mDhcpState);
- addState(mDhcpHaveLeaseState, mDhcpState);
- addState(mConfiguringInterfaceState, mDhcpHaveLeaseState);
- addState(mDhcpBoundState, mDhcpHaveLeaseState);
- addState(mWaitBeforeRenewalState, mDhcpHaveLeaseState);
- addState(mDhcpRenewingState, mDhcpHaveLeaseState);
- addState(mDhcpRebindingState, mDhcpHaveLeaseState);
- addState(mDhcpInitRebootState, mDhcpState);
- addState(mDhcpRebootingState, mDhcpState);
-
- setInitialState(mStoppedState);
-
- mRandom = new Random();
-
- // Used to schedule packet retransmissions.
- mKickAlarm = makeWakeupMessage("KICK", CMD_KICK);
- // Used to time out PacketRetransmittingStates.
- mTimeoutAlarm = makeWakeupMessage("TIMEOUT", CMD_TIMEOUT);
- // Used to schedule DHCP reacquisition.
- mRenewAlarm = makeWakeupMessage("RENEW", CMD_RENEW_DHCP);
- mRebindAlarm = makeWakeupMessage("REBIND", CMD_REBIND_DHCP);
- mExpiryAlarm = makeWakeupMessage("EXPIRY", CMD_EXPIRE_DHCP);
- }
-
- public void registerForPreDhcpNotification() {
- mRegisteredForPreDhcpNotification = true;
- }
-
- public static DhcpClient makeDhcpClient(
- Context context, StateMachine controller, InterfaceParams ifParams) {
- DhcpClient client = new DhcpClient(context, controller, ifParams.name);
- client.mIface = ifParams;
- client.start();
- return client;
- }
-
- private boolean initInterface() {
- if (mIface == null) mIface = InterfaceParams.getByName(mIfaceName);
- if (mIface == null) {
- Log.e(TAG, "Can't determine InterfaceParams for " + mIfaceName);
- return false;
- }
-
- mHwAddr = mIface.macAddr.toByteArray();
- mInterfaceBroadcastAddr = makePacketSocketAddress(mIface.index, DhcpPacket.ETHER_BROADCAST);
- return true;
- }
-
- private void startNewTransaction() {
- mTransactionId = mRandom.nextInt();
- mTransactionStartMillis = SystemClock.elapsedRealtime();
- }
-
- private boolean initSockets() {
- return initPacketSocket() && initUdpSocket();
- }
-
- private boolean initPacketSocket() {
- try {
- mPacketSock = Os.socket(AF_PACKET, SOCK_RAW, ETH_P_IP);
- SocketAddress addr = makePacketSocketAddress((short) ETH_P_IP, mIface.index);
- Os.bind(mPacketSock, addr);
- NetworkUtils.attachDhcpFilter(mPacketSock);
- } catch(SocketException|ErrnoException e) {
- Log.e(TAG, "Error creating packet socket", e);
- return false;
- }
- return true;
- }
-
- private boolean initUdpSocket() {
- final int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_DHCP);
- try {
- mUdpSock = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
- SocketUtils.bindSocketToInterface(mUdpSock, mIfaceName);
- Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_REUSEADDR, 1);
- Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_BROADCAST, 1);
- Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_RCVBUF, 0);
- Os.bind(mUdpSock, Inet4Address.ANY, DhcpPacket.DHCP_CLIENT);
- } catch(SocketException|ErrnoException e) {
- Log.e(TAG, "Error creating UDP socket", e);
- return false;
- } finally {
- TrafficStats.setThreadStatsTag(oldTag);
- }
- return true;
- }
-
- private boolean connectUdpSock(Inet4Address to) {
- try {
- Os.connect(mUdpSock, to, DhcpPacket.DHCP_SERVER);
- return true;
- } catch (SocketException|ErrnoException e) {
- Log.e(TAG, "Error connecting UDP socket", e);
- return false;
- }
- }
-
- private static void closeQuietly(FileDescriptor fd) {
- try {
- IoBridge.closeAndSignalBlockedThreads(fd);
- } catch (IOException ignored) {}
- }
-
- private void closeSockets() {
- closeQuietly(mUdpSock);
- closeQuietly(mPacketSock);
- }
-
- class ReceiveThread extends Thread {
-
- private final byte[] mPacket = new byte[DhcpPacket.MAX_LENGTH];
- private volatile boolean mStopped = false;
-
- public void halt() {
- mStopped = true;
- closeSockets(); // Interrupts the read() call the thread is blocked in.
- }
-
- @Override
- public void run() {
- if (DBG) Log.d(TAG, "Receive thread started");
- while (!mStopped) {
- int length = 0; // Or compiler can't tell it's initialized if a parse error occurs.
- try {
- length = Os.read(mPacketSock, mPacket, 0, mPacket.length);
- DhcpPacket packet = null;
- packet = DhcpPacket.decodeFullPacket(mPacket, length, DhcpPacket.ENCAP_L2);
- if (DBG) Log.d(TAG, "Received packet: " + packet);
- sendMessage(CMD_RECEIVED_PACKET, packet);
- } catch (IOException|ErrnoException e) {
- if (!mStopped) {
- Log.e(TAG, "Read error", e);
- logError(DhcpErrorEvent.RECEIVE_ERROR);
- }
- } catch (DhcpPacket.ParseException e) {
- Log.e(TAG, "Can't parse packet: " + e.getMessage());
- if (PACKET_DBG) {
- Log.d(TAG, HexDump.dumpHexString(mPacket, 0, length));
- }
- if (e.errorCode == DhcpErrorEvent.DHCP_NO_COOKIE) {
- int snetTagId = 0x534e4554;
- String bugId = "31850211";
- int uid = -1;
- String data = DhcpPacket.ParseException.class.getName();
- EventLog.writeEvent(snetTagId, bugId, uid, data);
- }
- logError(e.errorCode);
- }
- }
- if (DBG) Log.d(TAG, "Receive thread stopped");
- }
- }
-
- private short getSecs() {
- return (short) ((SystemClock.elapsedRealtime() - mTransactionStartMillis) / 1000);
- }
-
- private boolean transmitPacket(ByteBuffer buf, String description, int encap, Inet4Address to) {
- try {
- if (encap == DhcpPacket.ENCAP_L2) {
- if (DBG) Log.d(TAG, "Broadcasting " + description);
- Os.sendto(mPacketSock, buf.array(), 0, buf.limit(), 0, mInterfaceBroadcastAddr);
- } else if (encap == DhcpPacket.ENCAP_BOOTP && to.equals(INADDR_BROADCAST)) {
- if (DBG) Log.d(TAG, "Broadcasting " + description);
- // We only send L3-encapped broadcasts in DhcpRebindingState,
- // where we have an IP address and an unconnected UDP socket.
- //
- // N.B.: We only need this codepath because DhcpRequestPacket
- // hardcodes the source IP address to 0.0.0.0. We could reuse
- // the packet socket if this ever changes.
- Os.sendto(mUdpSock, buf, 0, to, DhcpPacket.DHCP_SERVER);
- } else {
- // It's safe to call getpeername here, because we only send unicast packets if we
- // have an IP address, and we connect the UDP socket in DhcpBoundState#enter.
- if (DBG) Log.d(TAG, String.format("Unicasting %s to %s",
- description, Os.getpeername(mUdpSock)));
- Os.write(mUdpSock, buf);
- }
- } catch(ErrnoException|IOException e) {
- Log.e(TAG, "Can't send packet: ", e);
- return false;
- }
- return true;
- }
-
- private boolean sendDiscoverPacket() {
- ByteBuffer packet = DhcpPacket.buildDiscoverPacket(
- DhcpPacket.ENCAP_L2, mTransactionId, getSecs(), mHwAddr,
- DO_UNICAST, REQUESTED_PARAMS);
- return transmitPacket(packet, "DHCPDISCOVER", DhcpPacket.ENCAP_L2, INADDR_BROADCAST);
- }
-
- private boolean sendRequestPacket(
- Inet4Address clientAddress, Inet4Address requestedAddress,
- Inet4Address serverAddress, Inet4Address to) {
- // TODO: should we use the transaction ID from the server?
- final int encap = INADDR_ANY.equals(clientAddress)
- ? DhcpPacket.ENCAP_L2 : DhcpPacket.ENCAP_BOOTP;
-
- ByteBuffer packet = DhcpPacket.buildRequestPacket(
- encap, mTransactionId, getSecs(), clientAddress,
- DO_UNICAST, mHwAddr, requestedAddress,
- serverAddress, REQUESTED_PARAMS, null);
- String serverStr = (serverAddress != null) ? serverAddress.getHostAddress() : null;
- String description = "DHCPREQUEST ciaddr=" + clientAddress.getHostAddress() +
- " request=" + requestedAddress.getHostAddress() +
- " serverid=" + serverStr;
- return transmitPacket(packet, description, encap, to);
- }
-
- private void scheduleLeaseTimers() {
- if (mDhcpLeaseExpiry == 0) {
- Log.d(TAG, "Infinite lease, no timer scheduling needed");
- return;
- }
-
- final long now = SystemClock.elapsedRealtime();
-
- // TODO: consider getting the renew and rebind timers from T1 and T2.
- // See also:
- // https://tools.ietf.org/html/rfc2131#section-4.4.5
- // https://tools.ietf.org/html/rfc1533#section-9.9
- // https://tools.ietf.org/html/rfc1533#section-9.10
- final long remainingDelay = mDhcpLeaseExpiry - now;
- final long renewDelay = remainingDelay / 2;
- final long rebindDelay = remainingDelay * 7 / 8;
- mRenewAlarm.schedule(now + renewDelay);
- mRebindAlarm.schedule(now + rebindDelay);
- mExpiryAlarm.schedule(now + remainingDelay);
- Log.d(TAG, "Scheduling renewal in " + (renewDelay / 1000) + "s");
- Log.d(TAG, "Scheduling rebind in " + (rebindDelay / 1000) + "s");
- Log.d(TAG, "Scheduling expiry in " + (remainingDelay / 1000) + "s");
- }
-
- private void notifySuccess() {
- mController.sendMessage(
- CMD_POST_DHCP_ACTION, DHCP_SUCCESS, 0, new DhcpResults(mDhcpLease));
- }
-
- private void notifyFailure() {
- mController.sendMessage(CMD_POST_DHCP_ACTION, DHCP_FAILURE, 0, null);
- }
-
- private void acceptDhcpResults(DhcpResults results, String msg) {
- mDhcpLease = results;
- mOffer = null;
- Log.d(TAG, msg + " lease: " + mDhcpLease);
- notifySuccess();
- }
-
- private void clearDhcpState() {
- mDhcpLease = null;
- mDhcpLeaseExpiry = 0;
- mOffer = null;
- }
-
- /**
- * Quit the DhcpStateMachine.
- *
- * @hide
- */
- public void doQuit() {
- Log.d(TAG, "doQuit");
- quit();
- }
-
- @Override
- protected void onQuitting() {
- Log.d(TAG, "onQuitting");
- mController.sendMessage(CMD_ON_QUIT);
- }
-
- abstract class LoggingState extends State {
- private long mEnterTimeMs;
-
- @Override
- public void enter() {
- if (STATE_DBG) Log.d(TAG, "Entering state " + getName());
- mEnterTimeMs = SystemClock.elapsedRealtime();
- }
-
- @Override
- public void exit() {
- long durationMs = SystemClock.elapsedRealtime() - mEnterTimeMs;
- logState(getName(), (int) durationMs);
- }
-
- private String messageName(int what) {
- return sMessageNames.get(what, Integer.toString(what));
- }
-
- private String messageToString(Message message) {
- long now = SystemClock.uptimeMillis();
- return new StringBuilder(" ")
- .append(message.getWhen() - now)
- .append(messageName(message.what))
- .append(" ").append(message.arg1)
- .append(" ").append(message.arg2)
- .append(" ").append(message.obj)
- .toString();
- }
-
- @Override
- public boolean processMessage(Message message) {
- if (MSG_DBG) {
- Log.d(TAG, getName() + messageToString(message));
- }
- return NOT_HANDLED;
- }
-
- @Override
- public String getName() {
- // All DhcpClient's states are inner classes with a well defined name.
- // Use getSimpleName() and avoid super's getName() creating new String instances.
- return getClass().getSimpleName();
- }
- }
-
- // Sends CMD_PRE_DHCP_ACTION to the controller, waits for the controller to respond with
- // CMD_PRE_DHCP_ACTION_COMPLETE, and then transitions to mOtherState.
- abstract class WaitBeforeOtherState extends LoggingState {
- protected State mOtherState;
-
- @Override
- public void enter() {
- super.enter();
- mController.sendMessage(CMD_PRE_DHCP_ACTION);
- }
-
- @Override
- public boolean processMessage(Message message) {
- super.processMessage(message);
- switch (message.what) {
- case CMD_PRE_DHCP_ACTION_COMPLETE:
- transitionTo(mOtherState);
- return HANDLED;
- default:
- return NOT_HANDLED;
- }
- }
- }
-
- class StoppedState extends State {
- @Override
- public boolean processMessage(Message message) {
- switch (message.what) {
- case CMD_START_DHCP:
- if (mRegisteredForPreDhcpNotification) {
- transitionTo(mWaitBeforeStartState);
- } else {
- transitionTo(mDhcpInitState);
- }
- return HANDLED;
- default:
- return NOT_HANDLED;
- }
- }
- }
-
- class WaitBeforeStartState extends WaitBeforeOtherState {
- public WaitBeforeStartState(State otherState) {
- super();
- mOtherState = otherState;
- }
- }
-
- class WaitBeforeRenewalState extends WaitBeforeOtherState {
- public WaitBeforeRenewalState(State otherState) {
- super();
- mOtherState = otherState;
- }
- }
-
- class DhcpState extends State {
- @Override
- public void enter() {
- clearDhcpState();
- if (initInterface() && initSockets()) {
- mReceiveThread = new ReceiveThread();
- mReceiveThread.start();
- } else {
- notifyFailure();
- transitionTo(mStoppedState);
- }
- }
-
- @Override
- public void exit() {
- if (mReceiveThread != null) {
- mReceiveThread.halt(); // Also closes sockets.
- mReceiveThread = null;
- }
- clearDhcpState();
- }
-
- @Override
- public boolean processMessage(Message message) {
- super.processMessage(message);
- switch (message.what) {
- case CMD_STOP_DHCP:
- transitionTo(mStoppedState);
- return HANDLED;
- default:
- return NOT_HANDLED;
- }
- }
- }
-
- public boolean isValidPacket(DhcpPacket packet) {
- // TODO: check checksum.
- int xid = packet.getTransactionId();
- if (xid != mTransactionId) {
- Log.d(TAG, "Unexpected transaction ID " + xid + ", expected " + mTransactionId);
- return false;
- }
- if (!Arrays.equals(packet.getClientMac(), mHwAddr)) {
- Log.d(TAG, "MAC addr mismatch: got " +
- HexDump.toHexString(packet.getClientMac()) + ", expected " +
- HexDump.toHexString(packet.getClientMac()));
- return false;
- }
- return true;
- }
-
- public void setDhcpLeaseExpiry(DhcpPacket packet) {
- long leaseTimeMillis = packet.getLeaseTimeMillis();
- mDhcpLeaseExpiry =
- (leaseTimeMillis > 0) ? SystemClock.elapsedRealtime() + leaseTimeMillis : 0;
- }
-
- /**
- * Retransmits packets using jittered exponential backoff with an optional timeout. Packet
- * transmission is triggered by CMD_KICK, which is sent by an AlarmManager alarm. If a subclass
- * sets mTimeout to a positive value, then timeout() is called by an AlarmManager alarm mTimeout
- * milliseconds after entering the state. Kicks and timeouts are cancelled when leaving the
- * state.
- *
- * Concrete subclasses must implement sendPacket, which is called when the alarm fires and a
- * packet needs to be transmitted, and receivePacket, which is triggered by CMD_RECEIVED_PACKET
- * sent by the receive thread. They may also set mTimeout and implement timeout.
- */
- abstract class PacketRetransmittingState extends LoggingState {
-
- private int mTimer;
- protected int mTimeout = 0;
-
- @Override
- public void enter() {
- super.enter();
- initTimer();
- maybeInitTimeout();
- sendMessage(CMD_KICK);
- }
-
- @Override
- public boolean processMessage(Message message) {
- super.processMessage(message);
- switch (message.what) {
- case CMD_KICK:
- sendPacket();
- scheduleKick();
- return HANDLED;
- case CMD_RECEIVED_PACKET:
- receivePacket((DhcpPacket) message.obj);
- return HANDLED;
- case CMD_TIMEOUT:
- timeout();
- return HANDLED;
- default:
- return NOT_HANDLED;
- }
- }
-
- @Override
- public void exit() {
- super.exit();
- mKickAlarm.cancel();
- mTimeoutAlarm.cancel();
- }
-
- abstract protected boolean sendPacket();
- abstract protected void receivePacket(DhcpPacket packet);
- protected void timeout() {}
-
- protected void initTimer() {
- mTimer = FIRST_TIMEOUT_MS;
- }
-
- protected int jitterTimer(int baseTimer) {
- int maxJitter = baseTimer / 10;
- int jitter = mRandom.nextInt(2 * maxJitter) - maxJitter;
- return baseTimer + jitter;
- }
-
- protected void scheduleKick() {
- long now = SystemClock.elapsedRealtime();
- long timeout = jitterTimer(mTimer);
- long alarmTime = now + timeout;
- mKickAlarm.schedule(alarmTime);
- mTimer *= 2;
- if (mTimer > MAX_TIMEOUT_MS) {
- mTimer = MAX_TIMEOUT_MS;
- }
- }
-
- protected void maybeInitTimeout() {
- if (mTimeout > 0) {
- long alarmTime = SystemClock.elapsedRealtime() + mTimeout;
- mTimeoutAlarm.schedule(alarmTime);
- }
- }
- }
-
- class DhcpInitState extends PacketRetransmittingState {
- public DhcpInitState() {
- super();
- }
-
- @Override
- public void enter() {
- super.enter();
- startNewTransaction();
- mLastInitEnterTime = SystemClock.elapsedRealtime();
- }
-
- protected boolean sendPacket() {
- return sendDiscoverPacket();
- }
-
- protected void receivePacket(DhcpPacket packet) {
- if (!isValidPacket(packet)) return;
- if (!(packet instanceof DhcpOfferPacket)) return;
- mOffer = packet.toDhcpResults();
- if (mOffer != null) {
- Log.d(TAG, "Got pending lease: " + mOffer);
- transitionTo(mDhcpRequestingState);
- }
- }
- }
-
- // Not implemented. We request the first offer we receive.
- class DhcpSelectingState extends LoggingState {
- }
-
- class DhcpRequestingState extends PacketRetransmittingState {
- public DhcpRequestingState() {
- mTimeout = DHCP_TIMEOUT_MS / 2;
- }
-
- protected boolean sendPacket() {
- return sendRequestPacket(
- INADDR_ANY, // ciaddr
- (Inet4Address) mOffer.ipAddress.getAddress(), // DHCP_REQUESTED_IP
- (Inet4Address) mOffer.serverAddress, // DHCP_SERVER_IDENTIFIER
- INADDR_BROADCAST); // packet destination address
- }
-
- protected void receivePacket(DhcpPacket packet) {
- if (!isValidPacket(packet)) return;
- if ((packet instanceof DhcpAckPacket)) {
- DhcpResults results = packet.toDhcpResults();
- if (results != null) {
- setDhcpLeaseExpiry(packet);
- acceptDhcpResults(results, "Confirmed");
- transitionTo(mConfiguringInterfaceState);
- }
- } else if (packet instanceof DhcpNakPacket) {
- // TODO: Wait a while before returning into INIT state.
- Log.d(TAG, "Received NAK, returning to INIT");
- mOffer = null;
- transitionTo(mDhcpInitState);
- }
- }
-
- @Override
- protected void timeout() {
- // After sending REQUESTs unsuccessfully for a while, go back to init.
- transitionTo(mDhcpInitState);
- }
- }
-
- class DhcpHaveLeaseState extends State {
- @Override
- public boolean processMessage(Message message) {
- switch (message.what) {
- case CMD_EXPIRE_DHCP:
- Log.d(TAG, "Lease expired!");
- notifyFailure();
- transitionTo(mDhcpInitState);
- return HANDLED;
- default:
- return NOT_HANDLED;
- }
- }
-
- @Override
- public void exit() {
- // Clear any extant alarms.
- mRenewAlarm.cancel();
- mRebindAlarm.cancel();
- mExpiryAlarm.cancel();
- clearDhcpState();
- // Tell IpManager to clear the IPv4 address. There is no need to
- // wait for confirmation since any subsequent packets are sent from
- // INADDR_ANY anyway (DISCOVER, REQUEST).
- mController.sendMessage(CMD_CLEAR_LINKADDRESS);
- }
- }
-
- class ConfiguringInterfaceState extends LoggingState {
- @Override
- public void enter() {
- super.enter();
- mController.sendMessage(CMD_CONFIGURE_LINKADDRESS, mDhcpLease.ipAddress);
- }
-
- @Override
- public boolean processMessage(Message message) {
- super.processMessage(message);
- switch (message.what) {
- case EVENT_LINKADDRESS_CONFIGURED:
- transitionTo(mDhcpBoundState);
- return HANDLED;
- default:
- return NOT_HANDLED;
- }
- }
- }
-
- class DhcpBoundState extends LoggingState {
- @Override
- public void enter() {
- super.enter();
- if (mDhcpLease.serverAddress != null && !connectUdpSock(mDhcpLease.serverAddress)) {
- // There's likely no point in going into DhcpInitState here, we'll probably
- // just repeat the transaction, get the same IP address as before, and fail.
- //
- // NOTE: It is observed that connectUdpSock() basically never fails, due to
- // SO_BINDTODEVICE. Examining the local socket address shows it will happily
- // return an IPv4 address from another interface, or even return "0.0.0.0".
- //
- // TODO: Consider deleting this check, following testing on several kernels.
- notifyFailure();
- transitionTo(mStoppedState);
- }
-
- scheduleLeaseTimers();
- logTimeToBoundState();
- }
-
- @Override
- public void exit() {
- super.exit();
- mLastBoundExitTime = SystemClock.elapsedRealtime();
- }
-
- @Override
- public boolean processMessage(Message message) {
- super.processMessage(message);
- switch (message.what) {
- case CMD_RENEW_DHCP:
- if (mRegisteredForPreDhcpNotification) {
- transitionTo(mWaitBeforeRenewalState);
- } else {
- transitionTo(mDhcpRenewingState);
- }
- return HANDLED;
- default:
- return NOT_HANDLED;
- }
- }
-
- private void logTimeToBoundState() {
- long now = SystemClock.elapsedRealtime();
- if (mLastBoundExitTime > mLastInitEnterTime) {
- logState(DhcpClientEvent.RENEWING_BOUND, (int)(now - mLastBoundExitTime));
- } else {
- logState(DhcpClientEvent.INITIAL_BOUND, (int)(now - mLastInitEnterTime));
- }
- }
- }
-
- abstract class DhcpReacquiringState extends PacketRetransmittingState {
- protected String mLeaseMsg;
-
- @Override
- public void enter() {
- super.enter();
- startNewTransaction();
- }
-
- abstract protected Inet4Address packetDestination();
-
- protected boolean sendPacket() {
- return sendRequestPacket(
- (Inet4Address) mDhcpLease.ipAddress.getAddress(), // ciaddr
- INADDR_ANY, // DHCP_REQUESTED_IP
- null, // DHCP_SERVER_IDENTIFIER
- packetDestination()); // packet destination address
- }
-
- protected void receivePacket(DhcpPacket packet) {
- if (!isValidPacket(packet)) return;
- if ((packet instanceof DhcpAckPacket)) {
- final DhcpResults results = packet.toDhcpResults();
- if (results != null) {
- if (!mDhcpLease.ipAddress.equals(results.ipAddress)) {
- Log.d(TAG, "Renewed lease not for our current IP address!");
- notifyFailure();
- transitionTo(mDhcpInitState);
- }
- setDhcpLeaseExpiry(packet);
- // Updating our notion of DhcpResults here only causes the
- // DNS servers and routes to be updated in LinkProperties
- // in IpManager and by any overridden relevant handlers of
- // the registered IpManager.Callback. IP address changes
- // are not supported here.
- acceptDhcpResults(results, mLeaseMsg);
- transitionTo(mDhcpBoundState);
- }
- } else if (packet instanceof DhcpNakPacket) {
- Log.d(TAG, "Received NAK, returning to INIT");
- notifyFailure();
- transitionTo(mDhcpInitState);
- }
- }
- }
-
- class DhcpRenewingState extends DhcpReacquiringState {
- public DhcpRenewingState() {
- mLeaseMsg = "Renewed";
- }
-
- @Override
- public boolean processMessage(Message message) {
- if (super.processMessage(message) == HANDLED) {
- return HANDLED;
- }
-
- switch (message.what) {
- case CMD_REBIND_DHCP:
- transitionTo(mDhcpRebindingState);
- return HANDLED;
- default:
- return NOT_HANDLED;
- }
- }
-
- @Override
- protected Inet4Address packetDestination() {
- // Not specifying a SERVER_IDENTIFIER option is a violation of RFC 2131, but...
- // http://b/25343517 . Try to make things work anyway by using broadcast renews.
- return (mDhcpLease.serverAddress != null) ?
- mDhcpLease.serverAddress : INADDR_BROADCAST;
- }
- }
-
- class DhcpRebindingState extends DhcpReacquiringState {
- public DhcpRebindingState() {
- mLeaseMsg = "Rebound";
- }
-
- @Override
- public void enter() {
- super.enter();
-
- // We need to broadcast and possibly reconnect the socket to a
- // completely different server.
- closeQuietly(mUdpSock);
- if (!initUdpSocket()) {
- Log.e(TAG, "Failed to recreate UDP socket");
- transitionTo(mDhcpInitState);
- }
- }
-
- @Override
- protected Inet4Address packetDestination() {
- return INADDR_BROADCAST;
- }
- }
-
- class DhcpInitRebootState extends LoggingState {
- }
-
- class DhcpRebootingState extends LoggingState {
- }
-
- private void logError(int errorCode) {
- mMetricsLog.log(mIfaceName, new DhcpErrorEvent(errorCode));
- }
-
- private void logState(String name, int durationMs) {
- final DhcpClientEvent event = new DhcpClientEvent.Builder()
- .setMsg(name)
- .setDurationMs(durationMs)
- .build();
- mMetricsLog.log(mIfaceName, event);
- }
}
diff --git a/services/net/java/android/net/ip/IpClient.java b/services/net/java/android/net/ip/IpClient.java
index 233b86f..a61c2ef 100644
--- a/services/net/java/android/net/ip/IpClient.java
+++ b/services/net/java/android/net/ip/IpClient.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,222 +16,206 @@
package android.net.ip;
-import static android.net.shared.LinkPropertiesParcelableUtil.fromStableParcelable;
+import static android.net.shared.LinkPropertiesParcelableUtil.toStableParcelable;
import android.content.Context;
-import android.net.ConnectivityManager;
-import android.net.DhcpResults;
-import android.net.INetd;
-import android.net.IpPrefix;
-import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.Network;
-import android.net.ProvisioningConfigurationParcelable;
import android.net.ProxyInfo;
-import android.net.ProxyInfoParcelable;
-import android.net.RouteInfo;
import android.net.StaticIpConfiguration;
import android.net.apf.ApfCapabilities;
-import android.net.apf.ApfFilter;
-import android.net.dhcp.DhcpClient;
-import android.net.metrics.IpConnectivityLog;
-import android.net.metrics.IpManagerEvent;
-import android.net.shared.InitialConfiguration;
-import android.net.util.InterfaceParams;
-import android.net.util.NetdService;
-import android.net.util.SharedLog;
import android.os.ConditionVariable;
import android.os.INetworkManagementService;
-import android.os.Message;
import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.SystemClock;
-import android.text.TextUtils;
-import android.util.LocalLog;
import android.util.Log;
-import android.util.SparseArray;
-
-import com.android.internal.R;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.IState;
-import com.android.internal.util.IndentingPrintWriter;
-import com.android.internal.util.MessageUtils;
-import com.android.internal.util.Preconditions;
-import com.android.internal.util.State;
-import com.android.internal.util.StateMachine;
-import com.android.internal.util.WakeupMessage;
-import com.android.server.net.NetlinkTracker;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.net.InetAddress;
-import java.util.Collection;
-import java.util.List;
-import java.util.Objects;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.CountDownLatch;
-import java.util.function.Predicate;
-import java.util.stream.Collectors;
-
/**
- * IpClient
- *
- * This class provides the interface to IP-layer provisioning and maintenance
- * functionality that can be used by transport layers like Wi-Fi, Ethernet,
- * et cetera.
- *
- * [ Lifetime ]
- * IpClient is designed to be instantiated as soon as the interface name is
- * known and can be as long-lived as the class containing it (i.e. declaring
- * it "private final" is okay).
- *
+ * Proxy for the IpClient in the NetworkStack. To be removed once clients are migrated.
* @hide
*/
-public class IpClient extends StateMachine {
- private static final boolean DBG = false;
+public class IpClient {
+ private static final String TAG = IpClient.class.getSimpleName();
+ private static final int IPCLIENT_BLOCK_TIMEOUT_MS = 10_000;
- // For message logging.
- private static final Class[] sMessageClasses = { IpClient.class, DhcpClient.class };
- private static final SparseArray<String> sWhatToString =
- MessageUtils.findMessageNames(sMessageClasses);
- // Two static concurrent hashmaps of interface name to logging classes.
- // One holds StateMachine logs and the other connectivity packet logs.
- private static final ConcurrentHashMap<String, SharedLog> sSmLogs = new ConcurrentHashMap<>();
- private static final ConcurrentHashMap<String, LocalLog> sPktLogs = new ConcurrentHashMap<>();
+ public static final String DUMP_ARG = "ipclient";
- // If |args| is non-empty, assume it's a list of interface names for which
- // we should print IpClient logs (filter out all others).
- public static void dumpAllLogs(PrintWriter writer, String[] args) {
- for (String ifname : sSmLogs.keySet()) {
- if (!ArrayUtils.isEmpty(args) && !ArrayUtils.contains(args, ifname)) continue;
+ private final ConditionVariable mIpClientCv;
+ private final ConditionVariable mShutdownCv;
- writer.println(String.format("--- BEGIN %s ---", ifname));
-
- final SharedLog smLog = sSmLogs.get(ifname);
- if (smLog != null) {
- writer.println("State machine log:");
- smLog.dump(null, writer, null);
- }
-
- writer.println("");
-
- final LocalLog pktLog = sPktLogs.get(ifname);
- if (pktLog != null) {
- writer.println("Connectivity packet log:");
- pktLog.readOnlyLocalLog().dump(null, writer, null);
- }
-
- writer.println(String.format("--- END %s ---", ifname));
- }
- }
+ private volatile IIpClient mIpClient;
/**
- * TODO: remove after migrating clients to use IpClientCallbacks directly
* @see IpClientCallbacks
*/
public static class Callback extends IpClientCallbacks {}
/**
- * TODO: remove once clients are migrated to IpClientUtil.WaitForProvisioningCallback
- * @see IpClientUtil.WaitForProvisioningCallbacks
+ * IpClient callback that allows clients to block until provisioning is complete.
*/
- public static class WaitForProvisioningCallback
- extends IpClientUtil.WaitForProvisioningCallbacks {}
+ public static class WaitForProvisioningCallback extends Callback {
+ private final ConditionVariable mCV = new ConditionVariable();
+ private LinkProperties mCallbackLinkProperties;
- // Use a wrapper class to log in order to ensure complete and detailed
- // logging. This method is lighter weight than annotations/reflection
- // and has the following benefits:
- //
- // - No invoked method can be forgotten.
- // Any new method added to IpClient.Callback must be overridden
- // here or it will never be called.
- //
- // - No invoking call site can be forgotten.
- // Centralized logging in this way means call sites don't need to
- // remember to log, and therefore no call site can be forgotten.
- //
- // - No variation in log format among call sites.
- // Encourages logging of any available arguments, and all call sites
- // are necessarily logged identically.
- //
- // NOTE: Log first because passed objects may or may not be thread-safe and
- // once passed on to the callback they may be modified by another thread.
- //
- // TODO: Find an lighter weight approach.
- private class LoggingCallbackWrapper extends IpClientCallbacks {
- private static final String PREFIX = "INVOKE ";
- private final IpClientCallbacks mCallback;
-
- LoggingCallbackWrapper(IpClientCallbacks callback) {
- mCallback = (callback != null) ? callback : new IpClientCallbacks();
+ /**
+ * Block until either {@link #onProvisioningSuccess(LinkProperties)} or
+ * {@link #onProvisioningFailure(LinkProperties)} is called.
+ */
+ public LinkProperties waitForProvisioning() {
+ mCV.block();
+ return mCallbackLinkProperties;
}
- private void log(String msg) {
- mLog.log(PREFIX + msg);
- }
-
- @Override
- public void onPreDhcpAction() {
- log("onPreDhcpAction()");
- mCallback.onPreDhcpAction();
- }
- @Override
- public void onPostDhcpAction() {
- log("onPostDhcpAction()");
- mCallback.onPostDhcpAction();
- }
- @Override
- public void onNewDhcpResults(DhcpResults dhcpResults) {
- log("onNewDhcpResults({" + dhcpResults + "})");
- mCallback.onNewDhcpResults(dhcpResults);
- }
@Override
public void onProvisioningSuccess(LinkProperties newLp) {
- log("onProvisioningSuccess({" + newLp + "})");
- mCallback.onProvisioningSuccess(newLp);
+ mCallbackLinkProperties = newLp;
+ mCV.open();
}
+
@Override
public void onProvisioningFailure(LinkProperties newLp) {
- log("onProvisioningFailure({" + newLp + "})");
- mCallback.onProvisioningFailure(newLp);
+ mCallbackLinkProperties = null;
+ mCV.open();
}
+ }
+
+ private class CallbackImpl extends IpClientUtil.IpClientCallbacksProxy {
+ /**
+ * Create a new IpClientCallbacksProxy.
+ */
+ CallbackImpl(IpClientCallbacks cb) {
+ super(cb);
+ }
+
@Override
- public void onLinkPropertiesChange(LinkProperties newLp) {
- log("onLinkPropertiesChange({" + newLp + "})");
- mCallback.onLinkPropertiesChange(newLp);
+ public void onIpClientCreated(IIpClient ipClient) {
+ mIpClient = ipClient;
+ mIpClientCv.open();
+ super.onIpClientCreated(ipClient);
}
- @Override
- public void onReachabilityLost(String logMsg) {
- log("onReachabilityLost(" + logMsg + ")");
- mCallback.onReachabilityLost(logMsg);
- }
+
@Override
public void onQuit() {
- log("onQuit()");
- mCallback.onQuit();
+ mShutdownCv.open();
+ super.onQuit();
}
- @Override
- public void installPacketFilter(byte[] filter) {
- log("installPacketFilter(byte[" + filter.length + "])");
- mCallback.installPacketFilter(filter);
+ }
+
+ /**
+ * Create a new IpClient.
+ */
+ public IpClient(Context context, String iface, Callback callback) {
+ mIpClientCv = new ConditionVariable(false);
+ mShutdownCv = new ConditionVariable(false);
+
+ IpClientUtil.makeIpClient(context, iface, new CallbackImpl(callback));
+ }
+
+ /**
+ * @see IpClient#IpClient(Context, String, IpClient.Callback)
+ */
+ public IpClient(Context context, String iface, Callback callback,
+ INetworkManagementService nms) {
+ this(context, iface, callback);
+ }
+
+ private interface IpClientAction {
+ void useIpClient(IIpClient ipClient) throws RemoteException;
+ }
+
+ private void doWithIpClient(IpClientAction action) {
+ mIpClientCv.block(IPCLIENT_BLOCK_TIMEOUT_MS);
+ try {
+ action.useIpClient(mIpClient);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error communicating with IpClient", e);
}
- @Override
- public void startReadPacketFilter() {
- log("startReadPacketFilter()");
- mCallback.startReadPacketFilter();
- }
- @Override
- public void setFallbackMulticastFilter(boolean enabled) {
- log("setFallbackMulticastFilter(" + enabled + ")");
- mCallback.setFallbackMulticastFilter(enabled);
- }
- @Override
- public void setNeighborDiscoveryOffload(boolean enable) {
- log("setNeighborDiscoveryOffload(" + enable + ")");
- mCallback.setNeighborDiscoveryOffload(enable);
- }
+ }
+
+ /**
+ * Notify IpClient that PreDhcpAction is completed.
+ */
+ public void completedPreDhcpAction() {
+ doWithIpClient(c -> c.completedPreDhcpAction());
+ }
+
+ /**
+ * Confirm the provisioning configuration.
+ */
+ public void confirmConfiguration() {
+ doWithIpClient(c -> c.confirmConfiguration());
+ }
+
+ /**
+ * Notify IpClient that packet filter read is complete.
+ */
+ public void readPacketFilterComplete(byte[] data) {
+ doWithIpClient(c -> c.readPacketFilterComplete(data));
+ }
+
+ /**
+ * Shutdown the IpClient altogether.
+ */
+ public void shutdown() {
+ doWithIpClient(c -> c.shutdown());
+ }
+
+ /**
+ * Start the IpClient provisioning.
+ */
+ public void startProvisioning(ProvisioningConfiguration config) {
+ doWithIpClient(c -> c.startProvisioning(config.toStableParcelable()));
+ }
+
+ /**
+ * Stop the IpClient.
+ */
+ public void stop() {
+ doWithIpClient(c -> c.stop());
+ }
+
+ /**
+ * Set the IpClient TCP buffer sizes.
+ */
+ public void setTcpBufferSizes(String tcpBufferSizes) {
+ doWithIpClient(c -> c.setTcpBufferSizes(tcpBufferSizes));
+ }
+
+ /**
+ * Set the IpClient HTTP proxy.
+ */
+ public void setHttpProxy(ProxyInfo proxyInfo) {
+ doWithIpClient(c -> c.setHttpProxy(toStableParcelable(proxyInfo)));
+ }
+
+ /**
+ * Set the IpClient multicast filter.
+ */
+ public void setMulticastFilter(boolean enabled) {
+ doWithIpClient(c -> c.setMulticastFilter(enabled));
+ }
+
+ /**
+ * Dump IpClient logs.
+ */
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ doWithIpClient(c -> IpClientUtil.dumpIpClient(c, fd, pw, args));
+ }
+
+ /**
+ * Block until IpClient shutdown.
+ */
+ public void awaitShutdown() {
+ mShutdownCv.block(IPCLIENT_BLOCK_TIMEOUT_MS);
+ }
+
+ /**
+ * Create a new ProvisioningConfiguration.
+ */
+ public static ProvisioningConfiguration.Builder buildProvisioningConfiguration() {
+ return new ProvisioningConfiguration.Builder();
}
/**
@@ -333,1430 +317,4 @@
}
}
}
-
- public static final String DUMP_ARG = "ipclient";
- public static final String DUMP_ARG_CONFIRM = "confirm";
-
- private static final int CMD_TERMINATE_AFTER_STOP = 1;
- private static final int CMD_STOP = 2;
- private static final int CMD_START = 3;
- private static final int CMD_CONFIRM = 4;
- private static final int EVENT_PRE_DHCP_ACTION_COMPLETE = 5;
- // Triggered by NetlinkTracker to communicate netlink events.
- private static final int EVENT_NETLINK_LINKPROPERTIES_CHANGED = 6;
- private static final int CMD_UPDATE_TCP_BUFFER_SIZES = 7;
- private static final int CMD_UPDATE_HTTP_PROXY = 8;
- private static final int CMD_SET_MULTICAST_FILTER = 9;
- private static final int EVENT_PROVISIONING_TIMEOUT = 10;
- private static final int EVENT_DHCPACTION_TIMEOUT = 11;
- private static final int EVENT_READ_PACKET_FILTER_COMPLETE = 12;
-
- // Internal commands to use instead of trying to call transitionTo() inside
- // a given State's enter() method. Calling transitionTo() from enter/exit
- // encounters a Log.wtf() that can cause trouble on eng builds.
- private static final int CMD_JUMP_STARTED_TO_RUNNING = 100;
- private static final int CMD_JUMP_RUNNING_TO_STOPPING = 101;
- private static final int CMD_JUMP_STOPPING_TO_STOPPED = 102;
-
- // IpClient shares a handler with DhcpClient: commands must not overlap
- public static final int DHCPCLIENT_CMD_BASE = 1000;
-
- private static final int MAX_LOG_RECORDS = 500;
- private static final int MAX_PACKET_RECORDS = 100;
-
- private static final boolean NO_CALLBACKS = false;
- private static final boolean SEND_CALLBACKS = true;
-
- // This must match the interface prefix in clatd.c.
- // TODO: Revert this hack once IpClient and Nat464Xlat work in concert.
- private static final String CLAT_PREFIX = "v4-";
-
- private static final int IMMEDIATE_FAILURE_DURATION = 0;
-
- private static final int PROV_CHANGE_STILL_NOT_PROVISIONED = 1;
- private static final int PROV_CHANGE_LOST_PROVISIONING = 2;
- private static final int PROV_CHANGE_GAINED_PROVISIONING = 3;
- private static final int PROV_CHANGE_STILL_PROVISIONED = 4;
-
- private final State mStoppedState = new StoppedState();
- private final State mStoppingState = new StoppingState();
- private final State mStartedState = new StartedState();
- private final State mRunningState = new RunningState();
-
- private final String mTag;
- private final Context mContext;
- private final String mInterfaceName;
- private final String mClatInterfaceName;
- @VisibleForTesting
- protected final IpClientCallbacks mCallback;
- private final Dependencies mDependencies;
- private final CountDownLatch mShutdownLatch;
- private final ConnectivityManager mCm;
- private final INetworkManagementService mNwService;
- private final NetlinkTracker mNetlinkTracker;
- private final WakeupMessage mProvisioningTimeoutAlarm;
- private final WakeupMessage mDhcpActionTimeoutAlarm;
- private final SharedLog mLog;
- private final LocalLog mConnectivityPacketLog;
- private final MessageHandlingLogger mMsgStateLogger;
- private final IpConnectivityLog mMetricsLog = new IpConnectivityLog();
- private final InterfaceController mInterfaceCtrl;
-
- private InterfaceParams mInterfaceParams;
-
- /**
- * Non-final member variables accessed only from within our StateMachine.
- */
- private LinkProperties mLinkProperties;
- private android.net.shared.ProvisioningConfiguration mConfiguration;
- private IpReachabilityMonitor mIpReachabilityMonitor;
- private DhcpClient mDhcpClient;
- private DhcpResults mDhcpResults;
- private String mTcpBufferSizes;
- private ProxyInfo mHttpProxy;
- private ApfFilter mApfFilter;
- private boolean mMulticastFiltering;
- private long mStartTimeMillis;
-
- /**
- * Reading the snapshot is an asynchronous operation initiated by invoking
- * Callback.startReadPacketFilter() and completed when the WiFi Service responds with an
- * EVENT_READ_PACKET_FILTER_COMPLETE message. The mApfDataSnapshotComplete condition variable
- * signals when a new snapshot is ready.
- */
- private final ConditionVariable mApfDataSnapshotComplete = new ConditionVariable();
-
- public static class Dependencies {
- public INetworkManagementService getNMS() {
- return INetworkManagementService.Stub.asInterface(
- ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
- }
-
- public INetd getNetd() {
- return NetdService.getInstance();
- }
-
- /**
- * Get interface parameters for the specified interface.
- */
- public InterfaceParams getInterfaceParams(String ifname) {
- return InterfaceParams.getByName(ifname);
- }
- }
-
- public IpClient(Context context, String ifName, IpClientCallbacks callback) {
- this(context, ifName, callback, new Dependencies());
- }
-
- /**
- * An expanded constructor, useful for dependency injection.
- * TODO: migrate all test users to mock IpClient directly and remove this ctor.
- */
- public IpClient(Context context, String ifName, IpClientCallbacks callback,
- INetworkManagementService nwService) {
- this(context, ifName, callback, new Dependencies() {
- @Override
- public INetworkManagementService getNMS() {
- return nwService;
- }
- });
- }
-
- @VisibleForTesting
- IpClient(Context context, String ifName, IpClientCallbacks callback, Dependencies deps) {
- super(IpClient.class.getSimpleName() + "." + ifName);
- Preconditions.checkNotNull(ifName);
- Preconditions.checkNotNull(callback);
-
- mTag = getName();
-
- mContext = context;
- mInterfaceName = ifName;
- mClatInterfaceName = CLAT_PREFIX + ifName;
- mCallback = new LoggingCallbackWrapper(callback);
- mDependencies = deps;
- mShutdownLatch = new CountDownLatch(1);
- mCm = mContext.getSystemService(ConnectivityManager.class);
- mNwService = deps.getNMS();
-
- sSmLogs.putIfAbsent(mInterfaceName, new SharedLog(MAX_LOG_RECORDS, mTag));
- mLog = sSmLogs.get(mInterfaceName);
- sPktLogs.putIfAbsent(mInterfaceName, new LocalLog(MAX_PACKET_RECORDS));
- mConnectivityPacketLog = sPktLogs.get(mInterfaceName);
- mMsgStateLogger = new MessageHandlingLogger();
-
- // TODO: Consider creating, constructing, and passing in some kind of
- // InterfaceController.Dependencies class.
- mInterfaceCtrl = new InterfaceController(mInterfaceName, deps.getNetd(), mLog);
-
- mNetlinkTracker = new NetlinkTracker(
- mInterfaceName,
- new NetlinkTracker.Callback() {
- @Override
- public void update() {
- sendMessage(EVENT_NETLINK_LINKPROPERTIES_CHANGED);
- }
- }) {
- @Override
- public void interfaceAdded(String iface) {
- super.interfaceAdded(iface);
- if (mClatInterfaceName.equals(iface)) {
- mCallback.setNeighborDiscoveryOffload(false);
- } else if (!mInterfaceName.equals(iface)) {
- return;
- }
-
- final String msg = "interfaceAdded(" + iface + ")";
- logMsg(msg);
- }
-
- @Override
- public void interfaceRemoved(String iface) {
- super.interfaceRemoved(iface);
- // TODO: Also observe mInterfaceName going down and take some
- // kind of appropriate action.
- if (mClatInterfaceName.equals(iface)) {
- // TODO: consider sending a message to the IpClient main
- // StateMachine thread, in case "NDO enabled" state becomes
- // tied to more things that 464xlat operation.
- mCallback.setNeighborDiscoveryOffload(true);
- } else if (!mInterfaceName.equals(iface)) {
- return;
- }
-
- final String msg = "interfaceRemoved(" + iface + ")";
- logMsg(msg);
- }
-
- private void logMsg(String msg) {
- Log.d(mTag, msg);
- getHandler().post(() -> mLog.log("OBSERVED " + msg));
- }
- };
-
- mLinkProperties = new LinkProperties();
- mLinkProperties.setInterfaceName(mInterfaceName);
-
- mProvisioningTimeoutAlarm = new WakeupMessage(mContext, getHandler(),
- mTag + ".EVENT_PROVISIONING_TIMEOUT", EVENT_PROVISIONING_TIMEOUT);
- mDhcpActionTimeoutAlarm = new WakeupMessage(mContext, getHandler(),
- mTag + ".EVENT_DHCPACTION_TIMEOUT", EVENT_DHCPACTION_TIMEOUT);
-
- // Anything the StateMachine may access must have been instantiated
- // before this point.
- configureAndStartStateMachine();
-
- // Anything that may send messages to the StateMachine must only be
- // configured to do so after the StateMachine has started (above).
- startStateMachineUpdaters();
- }
-
- /**
- * Make a IIpClient connector to communicate with this IpClient.
- */
- public IIpClient makeConnector() {
- return new IpClientConnector();
- }
-
- class IpClientConnector extends IIpClient.Stub {
- @Override
- public void completedPreDhcpAction() {
- IpClient.this.completedPreDhcpAction();
- }
- @Override
- public void confirmConfiguration() {
- IpClient.this.confirmConfiguration();
- }
- @Override
- public void readPacketFilterComplete(byte[] data) {
- IpClient.this.readPacketFilterComplete(data);
- }
- @Override
- public void shutdown() {
- IpClient.this.shutdown();
- }
- @Override
- public void startProvisioning(ProvisioningConfigurationParcelable req) {
- IpClient.this.startProvisioning(
- android.net.shared.ProvisioningConfiguration.fromStableParcelable(req));
- }
- @Override
- public void stop() {
- IpClient.this.stop();
- }
- @Override
- public void setTcpBufferSizes(String tcpBufferSizes) {
- IpClient.this.setTcpBufferSizes(tcpBufferSizes);
- }
- @Override
- public void setHttpProxy(ProxyInfoParcelable proxyInfo) {
- IpClient.this.setHttpProxy(fromStableParcelable(proxyInfo));
- }
- @Override
- public void setMulticastFilter(boolean enabled) {
- IpClient.this.setMulticastFilter(enabled);
- }
- // TODO: remove and have IpClient logs dumped in NetworkStack dumpsys
- public void dumpIpClientLogs(FileDescriptor fd, PrintWriter pw, String[] args) {
- IpClient.this.dump(fd, pw, args);
- }
- }
-
- private void configureAndStartStateMachine() {
- // CHECKSTYLE:OFF IndentationCheck
- addState(mStoppedState);
- addState(mStartedState);
- addState(mRunningState, mStartedState);
- addState(mStoppingState);
- // CHECKSTYLE:ON IndentationCheck
-
- setInitialState(mStoppedState);
-
- super.start();
- }
-
- private void startStateMachineUpdaters() {
- try {
- mNwService.registerObserver(mNetlinkTracker);
- } catch (RemoteException e) {
- logError("Couldn't register NetlinkTracker: %s", e);
- }
- }
-
- private void stopStateMachineUpdaters() {
- try {
- mNwService.unregisterObserver(mNetlinkTracker);
- } catch (RemoteException e) {
- logError("Couldn't unregister NetlinkTracker: %s", e);
- }
- }
-
- @Override
- protected void onQuitting() {
- mCallback.onQuit();
- mShutdownLatch.countDown();
- }
-
- /**
- * Shut down this IpClient instance altogether.
- */
- public void shutdown() {
- stop();
- sendMessage(CMD_TERMINATE_AFTER_STOP);
- }
-
- // In order to avoid deadlock, this method MUST NOT be called on the
- // IpClient instance's thread. This prohibition includes code executed by
- // when methods on the passed-in IpClient.Callback instance are called.
- public void awaitShutdown() {
- try {
- mShutdownLatch.await();
- } catch (InterruptedException e) {
- mLog.e("Interrupted while awaiting shutdown: " + e);
- }
- }
-
- public static ProvisioningConfiguration.Builder buildProvisioningConfiguration() {
- return new ProvisioningConfiguration.Builder();
- }
-
- /**
- * Start provisioning with the provided parameters.
- */
- public void startProvisioning(android.net.shared.ProvisioningConfiguration req) {
- if (!req.isValid()) {
- doImmediateProvisioningFailure(IpManagerEvent.ERROR_INVALID_PROVISIONING);
- return;
- }
-
- mInterfaceParams = mDependencies.getInterfaceParams(mInterfaceName);
- if (mInterfaceParams == null) {
- logError("Failed to find InterfaceParams for " + mInterfaceName);
- doImmediateProvisioningFailure(IpManagerEvent.ERROR_INTERFACE_NOT_FOUND);
- return;
- }
-
- mCallback.setNeighborDiscoveryOffload(true);
- sendMessage(CMD_START, new android.net.shared.ProvisioningConfiguration(req));
- }
-
- // TODO: Delete this.
- public void startProvisioning(StaticIpConfiguration staticIpConfig) {
- startProvisioning(buildProvisioningConfiguration()
- .withStaticConfiguration(staticIpConfig)
- .build());
- }
-
- public void startProvisioning() {
- startProvisioning(new android.net.shared.ProvisioningConfiguration());
- }
-
- /**
- * Stop this IpClient.
- *
- * <p>This does not shut down the StateMachine itself, which is handled by {@link #shutdown()}.
- */
- public void stop() {
- sendMessage(CMD_STOP);
- }
-
- /**
- * Confirm the provisioning configuration.
- */
- public void confirmConfiguration() {
- sendMessage(CMD_CONFIRM);
- }
-
- /**
- * For clients using {@link ProvisioningConfiguration.Builder#withPreDhcpAction()}, must be
- * called after {@link IIpClientCallbacks#onPreDhcpAction} to indicate that DHCP is clear to
- * proceed.
- */
- public void completedPreDhcpAction() {
- sendMessage(EVENT_PRE_DHCP_ACTION_COMPLETE);
- }
-
- /**
- * Indicate that packet filter read is complete.
- */
- public void readPacketFilterComplete(byte[] data) {
- sendMessage(EVENT_READ_PACKET_FILTER_COMPLETE, data);
- }
-
- /**
- * Set the TCP buffer sizes to use.
- *
- * This may be called, repeatedly, at any time before or after a call to
- * #startProvisioning(). The setting is cleared upon calling #stop().
- */
- public void setTcpBufferSizes(String tcpBufferSizes) {
- sendMessage(CMD_UPDATE_TCP_BUFFER_SIZES, tcpBufferSizes);
- }
-
- /**
- * Set the HTTP Proxy configuration to use.
- *
- * This may be called, repeatedly, at any time before or after a call to
- * #startProvisioning(). The setting is cleared upon calling #stop().
- */
- public void setHttpProxy(ProxyInfo proxyInfo) {
- sendMessage(CMD_UPDATE_HTTP_PROXY, proxyInfo);
- }
-
- /**
- * Enable or disable the multicast filter. Attempts to use APF to accomplish the filtering,
- * if not, Callback.setFallbackMulticastFilter() is called.
- */
- public void setMulticastFilter(boolean enabled) {
- sendMessage(CMD_SET_MULTICAST_FILTER, enabled);
- }
-
- /**
- * Dump logs of this IpClient.
- */
- public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
- if (args != null && args.length > 0 && DUMP_ARG_CONFIRM.equals(args[0])) {
- // Execute confirmConfiguration() and take no further action.
- confirmConfiguration();
- return;
- }
-
- // Thread-unsafe access to mApfFilter but just used for debugging.
- final ApfFilter apfFilter = mApfFilter;
- final android.net.shared.ProvisioningConfiguration provisioningConfig = mConfiguration;
- final ApfCapabilities apfCapabilities = (provisioningConfig != null)
- ? provisioningConfig.mApfCapabilities : null;
-
- IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
- pw.println(mTag + " APF dump:");
- pw.increaseIndent();
- if (apfFilter != null) {
- if (apfCapabilities.hasDataAccess()) {
- // Request a new snapshot, then wait for it.
- mApfDataSnapshotComplete.close();
- mCallback.startReadPacketFilter();
- if (!mApfDataSnapshotComplete.block(1000)) {
- pw.print("TIMEOUT: DUMPING STALE APF SNAPSHOT");
- }
- }
- apfFilter.dump(pw);
-
- } else {
- pw.print("No active ApfFilter; ");
- if (provisioningConfig == null) {
- pw.println("IpClient not yet started.");
- } else if (apfCapabilities == null || apfCapabilities.apfVersionSupported == 0) {
- pw.println("Hardware does not support APF.");
- } else {
- pw.println("ApfFilter not yet started, APF capabilities: " + apfCapabilities);
- }
- }
- pw.decreaseIndent();
- pw.println();
- pw.println(mTag + " current ProvisioningConfiguration:");
- pw.increaseIndent();
- pw.println(Objects.toString(provisioningConfig, "N/A"));
- pw.decreaseIndent();
-
- final IpReachabilityMonitor iprm = mIpReachabilityMonitor;
- if (iprm != null) {
- pw.println();
- pw.println(mTag + " current IpReachabilityMonitor state:");
- pw.increaseIndent();
- iprm.dump(pw);
- pw.decreaseIndent();
- }
-
- pw.println();
- pw.println(mTag + " StateMachine dump:");
- pw.increaseIndent();
- mLog.dump(fd, pw, args);
- pw.decreaseIndent();
-
- pw.println();
- pw.println(mTag + " connectivity packet log:");
- pw.println();
- pw.println("Debug with python and scapy via:");
- pw.println("shell$ python");
- pw.println(">>> from scapy import all as scapy");
- pw.println(">>> scapy.Ether(\"<paste_hex_string>\".decode(\"hex\")).show2()");
- pw.println();
-
- pw.increaseIndent();
- mConnectivityPacketLog.readOnlyLocalLog().dump(fd, pw, args);
- pw.decreaseIndent();
- }
-
-
- /**
- * Internals.
- */
-
- @Override
- protected String getWhatToString(int what) {
- return sWhatToString.get(what, "UNKNOWN: " + Integer.toString(what));
- }
-
- @Override
- protected String getLogRecString(Message msg) {
- final String logLine = String.format(
- "%s/%d %d %d %s [%s]",
- mInterfaceName, (mInterfaceParams == null) ? -1 : mInterfaceParams.index,
- msg.arg1, msg.arg2, Objects.toString(msg.obj), mMsgStateLogger);
-
- final String richerLogLine = getWhatToString(msg.what) + " " + logLine;
- mLog.log(richerLogLine);
- if (DBG) {
- Log.d(mTag, richerLogLine);
- }
-
- mMsgStateLogger.reset();
- return logLine;
- }
-
- @Override
- protected boolean recordLogRec(Message msg) {
- // Don't log EVENT_NETLINK_LINKPROPERTIES_CHANGED. They can be noisy,
- // and we already log any LinkProperties change that results in an
- // invocation of IpClient.Callback#onLinkPropertiesChange().
- final boolean shouldLog = (msg.what != EVENT_NETLINK_LINKPROPERTIES_CHANGED);
- if (!shouldLog) {
- mMsgStateLogger.reset();
- }
- return shouldLog;
- }
-
- private void logError(String fmt, Object... args) {
- final String msg = "ERROR " + String.format(fmt, args);
- Log.e(mTag, msg);
- mLog.log(msg);
- }
-
- // This needs to be called with care to ensure that our LinkProperties
- // are in sync with the actual LinkProperties of the interface. For example,
- // we should only call this if we know for sure that there are no IP addresses
- // assigned to the interface, etc.
- private void resetLinkProperties() {
- mNetlinkTracker.clearLinkProperties();
- mConfiguration = null;
- mDhcpResults = null;
- mTcpBufferSizes = "";
- mHttpProxy = null;
-
- mLinkProperties = new LinkProperties();
- mLinkProperties.setInterfaceName(mInterfaceName);
- }
-
- private void recordMetric(final int type) {
- // We may record error metrics prior to starting.
- // Map this to IMMEDIATE_FAILURE_DURATION.
- final long duration = (mStartTimeMillis > 0)
- ? (SystemClock.elapsedRealtime() - mStartTimeMillis)
- : IMMEDIATE_FAILURE_DURATION;
- mMetricsLog.log(mInterfaceName, new IpManagerEvent(type, duration));
- }
-
- // For now: use WifiStateMachine's historical notion of provisioned.
- @VisibleForTesting
- static boolean isProvisioned(LinkProperties lp, InitialConfiguration config) {
- // For historical reasons, we should connect even if all we have is
- // an IPv4 address and nothing else.
- if (lp.hasIPv4Address() || lp.isProvisioned()) {
- return true;
- }
- if (config == null) {
- return false;
- }
-
- // When an InitialConfiguration is specified, ignore any difference with previous
- // properties and instead check if properties observed match the desired properties.
- return config.isProvisionedBy(lp.getLinkAddresses(), lp.getRoutes());
- }
-
- // TODO: Investigate folding all this into the existing static function
- // LinkProperties.compareProvisioning() or some other single function that
- // takes two LinkProperties objects and returns a ProvisioningChange
- // object that is a correct and complete assessment of what changed, taking
- // account of the asymmetries described in the comments in this function.
- // Then switch to using it everywhere (IpReachabilityMonitor, etc.).
- private int compareProvisioning(LinkProperties oldLp, LinkProperties newLp) {
- int delta;
- InitialConfiguration config = mConfiguration != null ? mConfiguration.mInitialConfig : null;
- final boolean wasProvisioned = isProvisioned(oldLp, config);
- final boolean isProvisioned = isProvisioned(newLp, config);
-
- if (!wasProvisioned && isProvisioned) {
- delta = PROV_CHANGE_GAINED_PROVISIONING;
- } else if (wasProvisioned && isProvisioned) {
- delta = PROV_CHANGE_STILL_PROVISIONED;
- } else if (!wasProvisioned && !isProvisioned) {
- delta = PROV_CHANGE_STILL_NOT_PROVISIONED;
- } else {
- // (wasProvisioned && !isProvisioned)
- //
- // Note that this is true even if we lose a configuration element
- // (e.g., a default gateway) that would not be required to advance
- // into provisioned state. This is intended: if we have a default
- // router and we lose it, that's a sure sign of a problem, but if
- // we connect to a network with no IPv4 DNS servers, we consider
- // that to be a network without DNS servers and connect anyway.
- //
- // See the comment below.
- delta = PROV_CHANGE_LOST_PROVISIONING;
- }
-
- final boolean lostIPv6 = oldLp.isIPv6Provisioned() && !newLp.isIPv6Provisioned();
- final boolean lostIPv4Address = oldLp.hasIPv4Address() && !newLp.hasIPv4Address();
- final boolean lostIPv6Router = oldLp.hasIPv6DefaultRoute() && !newLp.hasIPv6DefaultRoute();
-
- // If bad wifi avoidance is disabled, then ignore IPv6 loss of
- // provisioning. Otherwise, when a hotspot that loses Internet
- // access sends out a 0-lifetime RA to its clients, the clients
- // will disconnect and then reconnect, avoiding the bad hotspot,
- // instead of getting stuck on the bad hotspot. http://b/31827713 .
- //
- // This is incorrect because if the hotspot then regains Internet
- // access with a different prefix, TCP connections on the
- // deprecated addresses will remain stuck.
- //
- // Note that we can still be disconnected by IpReachabilityMonitor
- // if the IPv6 default gateway (but not the IPv6 DNS servers; see
- // accompanying code in IpReachabilityMonitor) is unreachable.
- final boolean ignoreIPv6ProvisioningLoss =
- mConfiguration != null && mConfiguration.mUsingMultinetworkPolicyTracker
- && mCm.getAvoidBadWifi();
-
- // Additionally:
- //
- // Partial configurations (e.g., only an IPv4 address with no DNS
- // servers and no default route) are accepted as long as DHCPv4
- // succeeds. On such a network, isProvisioned() will always return
- // false, because the configuration is not complete, but we want to
- // connect anyway. It might be a disconnected network such as a
- // Chromecast or a wireless printer, for example.
- //
- // Because on such a network isProvisioned() will always return false,
- // delta will never be LOST_PROVISIONING. So check for loss of
- // provisioning here too.
- if (lostIPv4Address || (lostIPv6 && !ignoreIPv6ProvisioningLoss)) {
- delta = PROV_CHANGE_LOST_PROVISIONING;
- }
-
- // Additionally:
- //
- // If the previous link properties had a global IPv6 address and an
- // IPv6 default route then also consider the loss of that default route
- // to be a loss of provisioning. See b/27962810.
- if (oldLp.hasGlobalIPv6Address() && (lostIPv6Router && !ignoreIPv6ProvisioningLoss)) {
- delta = PROV_CHANGE_LOST_PROVISIONING;
- }
-
- return delta;
- }
-
- private void dispatchCallback(int delta, LinkProperties newLp) {
- switch (delta) {
- case PROV_CHANGE_GAINED_PROVISIONING:
- if (DBG) {
- Log.d(mTag, "onProvisioningSuccess()");
- }
- recordMetric(IpManagerEvent.PROVISIONING_OK);
- mCallback.onProvisioningSuccess(newLp);
- break;
-
- case PROV_CHANGE_LOST_PROVISIONING:
- if (DBG) {
- Log.d(mTag, "onProvisioningFailure()");
- }
- recordMetric(IpManagerEvent.PROVISIONING_FAIL);
- mCallback.onProvisioningFailure(newLp);
- break;
-
- default:
- if (DBG) {
- Log.d(mTag, "onLinkPropertiesChange()");
- }
- mCallback.onLinkPropertiesChange(newLp);
- break;
- }
- }
-
- // Updates all IpClient-related state concerned with LinkProperties.
- // Returns a ProvisioningChange for possibly notifying other interested
- // parties that are not fronted by IpClient.
- private int setLinkProperties(LinkProperties newLp) {
- if (mApfFilter != null) {
- mApfFilter.setLinkProperties(newLp);
- }
- if (mIpReachabilityMonitor != null) {
- mIpReachabilityMonitor.updateLinkProperties(newLp);
- }
-
- int delta = compareProvisioning(mLinkProperties, newLp);
- mLinkProperties = new LinkProperties(newLp);
-
- if (delta == PROV_CHANGE_GAINED_PROVISIONING) {
- // TODO: Add a proper ProvisionedState and cancel the alarm in
- // its enter() method.
- mProvisioningTimeoutAlarm.cancel();
- }
-
- return delta;
- }
-
- private LinkProperties assembleLinkProperties() {
- // [1] Create a new LinkProperties object to populate.
- LinkProperties newLp = new LinkProperties();
- newLp.setInterfaceName(mInterfaceName);
-
- // [2] Pull in data from netlink:
- // - IPv4 addresses
- // - IPv6 addresses
- // - IPv6 routes
- // - IPv6 DNS servers
- //
- // N.B.: this is fundamentally race-prone and should be fixed by
- // changing NetlinkTracker from a hybrid edge/level model to an
- // edge-only model, or by giving IpClient its own netlink socket(s)
- // so as to track all required information directly.
- LinkProperties netlinkLinkProperties = mNetlinkTracker.getLinkProperties();
- newLp.setLinkAddresses(netlinkLinkProperties.getLinkAddresses());
- for (RouteInfo route : netlinkLinkProperties.getRoutes()) {
- newLp.addRoute(route);
- }
- addAllReachableDnsServers(newLp, netlinkLinkProperties.getDnsServers());
-
- // [3] Add in data from DHCPv4, if available.
- //
- // mDhcpResults is never shared with any other owner so we don't have
- // to worry about concurrent modification.
- if (mDhcpResults != null) {
- for (RouteInfo route : mDhcpResults.getRoutes(mInterfaceName)) {
- newLp.addRoute(route);
- }
- addAllReachableDnsServers(newLp, mDhcpResults.dnsServers);
- newLp.setDomains(mDhcpResults.domains);
-
- if (mDhcpResults.mtu != 0) {
- newLp.setMtu(mDhcpResults.mtu);
- }
- }
-
- // [4] Add in TCP buffer sizes and HTTP Proxy config, if available.
- if (!TextUtils.isEmpty(mTcpBufferSizes)) {
- newLp.setTcpBufferSizes(mTcpBufferSizes);
- }
- if (mHttpProxy != null) {
- newLp.setHttpProxy(mHttpProxy);
- }
-
- // [5] Add data from InitialConfiguration
- if (mConfiguration != null && mConfiguration.mInitialConfig != null) {
- InitialConfiguration config = mConfiguration.mInitialConfig;
- // Add InitialConfiguration routes and dns server addresses once all addresses
- // specified in the InitialConfiguration have been observed with Netlink.
- if (config.isProvisionedBy(newLp.getLinkAddresses(), null)) {
- for (IpPrefix prefix : config.directlyConnectedRoutes) {
- newLp.addRoute(new RouteInfo(prefix, null, mInterfaceName));
- }
- }
- addAllReachableDnsServers(newLp, config.dnsServers);
- }
- final LinkProperties oldLp = mLinkProperties;
- if (DBG) {
- Log.d(mTag, String.format("Netlink-seen LPs: %s, new LPs: %s; old LPs: %s",
- netlinkLinkProperties, newLp, oldLp));
- }
-
- // TODO: also learn via netlink routes specified by an InitialConfiguration and specified
- // from a static IP v4 config instead of manually patching them in in steps [3] and [5].
- return newLp;
- }
-
- private static void addAllReachableDnsServers(
- LinkProperties lp, Iterable<InetAddress> dnses) {
- // TODO: Investigate deleting this reachability check. We should be
- // able to pass everything down to netd and let netd do evaluation
- // and RFC6724-style sorting.
- for (InetAddress dns : dnses) {
- if (!dns.isAnyLocalAddress() && lp.isReachable(dns)) {
- lp.addDnsServer(dns);
- }
- }
- }
-
- // Returns false if we have lost provisioning, true otherwise.
- private boolean handleLinkPropertiesUpdate(boolean sendCallbacks) {
- final LinkProperties newLp = assembleLinkProperties();
- if (Objects.equals(newLp, mLinkProperties)) {
- return true;
- }
- final int delta = setLinkProperties(newLp);
- if (sendCallbacks) {
- dispatchCallback(delta, newLp);
- }
- return (delta != PROV_CHANGE_LOST_PROVISIONING);
- }
-
- private void handleIPv4Success(DhcpResults dhcpResults) {
- mDhcpResults = new DhcpResults(dhcpResults);
- final LinkProperties newLp = assembleLinkProperties();
- final int delta = setLinkProperties(newLp);
-
- if (DBG) {
- Log.d(mTag, "onNewDhcpResults(" + Objects.toString(dhcpResults) + ")");
- }
- mCallback.onNewDhcpResults(dhcpResults);
- dispatchCallback(delta, newLp);
- }
-
- private void handleIPv4Failure() {
- // TODO: Investigate deleting this clearIPv4Address() call.
- //
- // DhcpClient will send us CMD_CLEAR_LINKADDRESS in all circumstances
- // that could trigger a call to this function. If we missed handling
- // that message in StartedState for some reason we would still clear
- // any addresses upon entry to StoppedState.
- mInterfaceCtrl.clearIPv4Address();
- mDhcpResults = null;
- if (DBG) {
- Log.d(mTag, "onNewDhcpResults(null)");
- }
- mCallback.onNewDhcpResults(null);
-
- handleProvisioningFailure();
- }
-
- private void handleProvisioningFailure() {
- final LinkProperties newLp = assembleLinkProperties();
- int delta = setLinkProperties(newLp);
- // If we've gotten here and we're still not provisioned treat that as
- // a total loss of provisioning.
- //
- // Either (a) static IP configuration failed or (b) DHCPv4 failed AND
- // there was no usable IPv6 obtained before a non-zero provisioning
- // timeout expired.
- //
- // Regardless: GAME OVER.
- if (delta == PROV_CHANGE_STILL_NOT_PROVISIONED) {
- delta = PROV_CHANGE_LOST_PROVISIONING;
- }
-
- dispatchCallback(delta, newLp);
- if (delta == PROV_CHANGE_LOST_PROVISIONING) {
- transitionTo(mStoppingState);
- }
- }
-
- private void doImmediateProvisioningFailure(int failureType) {
- logError("onProvisioningFailure(): %s", failureType);
- recordMetric(failureType);
- mCallback.onProvisioningFailure(new LinkProperties(mLinkProperties));
- }
-
- private boolean startIPv4() {
- // If we have a StaticIpConfiguration attempt to apply it and
- // handle the result accordingly.
- if (mConfiguration.mStaticIpConfig != null) {
- if (mInterfaceCtrl.setIPv4Address(mConfiguration.mStaticIpConfig.ipAddress)) {
- handleIPv4Success(new DhcpResults(mConfiguration.mStaticIpConfig));
- } else {
- return false;
- }
- } else {
- // Start DHCPv4.
- mDhcpClient = DhcpClient.makeDhcpClient(mContext, IpClient.this, mInterfaceParams);
- mDhcpClient.registerForPreDhcpNotification();
- mDhcpClient.sendMessage(DhcpClient.CMD_START_DHCP);
- }
-
- return true;
- }
-
- private boolean startIPv6() {
- return mInterfaceCtrl.setIPv6PrivacyExtensions(true)
- && mInterfaceCtrl.setIPv6AddrGenModeIfSupported(mConfiguration.mIPv6AddrGenMode)
- && mInterfaceCtrl.enableIPv6();
- }
-
- private boolean applyInitialConfig(InitialConfiguration config) {
- // TODO: also support specifying a static IPv4 configuration in InitialConfiguration.
- for (LinkAddress addr : findAll(config.ipAddresses, LinkAddress::isIPv6)) {
- if (!mInterfaceCtrl.addAddress(addr)) return false;
- }
-
- return true;
- }
-
- private boolean startIpReachabilityMonitor() {
- try {
- // TODO: Fetch these parameters from settings, and install a
- // settings observer to watch for update and re-program these
- // parameters (Q: is this level of dynamic updatability really
- // necessary or does reading from settings at startup suffice?).
- final int numSolicits = 5;
- final int interSolicitIntervalMs = 750;
- setNeighborParameters(mDependencies.getNetd(), mInterfaceName,
- numSolicits, interSolicitIntervalMs);
- } catch (Exception e) {
- mLog.e("Failed to adjust neighbor parameters", e);
- // Carry on using the system defaults (currently: 3, 1000);
- }
-
- try {
- mIpReachabilityMonitor = new IpReachabilityMonitor(
- mContext,
- mInterfaceParams,
- getHandler(),
- mLog,
- new IpReachabilityMonitor.Callback() {
- @Override
- public void notifyLost(InetAddress ip, String logMsg) {
- mCallback.onReachabilityLost(logMsg);
- }
- },
- mConfiguration.mUsingMultinetworkPolicyTracker);
- } catch (IllegalArgumentException iae) {
- // Failed to start IpReachabilityMonitor. Log it and call
- // onProvisioningFailure() immediately.
- //
- // See http://b/31038971.
- logError("IpReachabilityMonitor failure: %s", iae);
- mIpReachabilityMonitor = null;
- }
-
- return (mIpReachabilityMonitor != null);
- }
-
- private void stopAllIP() {
- // We don't need to worry about routes, just addresses, because:
- // - disableIpv6() will clear autoconf IPv6 routes as well, and
- // - we don't get IPv4 routes from netlink
- // so we neither react to nor need to wait for changes in either.
-
- mInterfaceCtrl.disableIPv6();
- mInterfaceCtrl.clearAllAddresses();
- }
-
- class StoppedState extends State {
- @Override
- public void enter() {
- stopAllIP();
-
- resetLinkProperties();
- if (mStartTimeMillis > 0) {
- // Completed a life-cycle; send a final empty LinkProperties
- // (cleared in resetLinkProperties() above) and record an event.
- mCallback.onLinkPropertiesChange(new LinkProperties(mLinkProperties));
- recordMetric(IpManagerEvent.COMPLETE_LIFECYCLE);
- mStartTimeMillis = 0;
- }
- }
-
- @Override
- public boolean processMessage(Message msg) {
- switch (msg.what) {
- case CMD_TERMINATE_AFTER_STOP:
- stopStateMachineUpdaters();
- quit();
- break;
-
- case CMD_STOP:
- break;
-
- case CMD_START:
- mConfiguration = (android.net.shared.ProvisioningConfiguration) msg.obj;
- transitionTo(mStartedState);
- break;
-
- case EVENT_NETLINK_LINKPROPERTIES_CHANGED:
- handleLinkPropertiesUpdate(NO_CALLBACKS);
- break;
-
- case CMD_UPDATE_TCP_BUFFER_SIZES:
- mTcpBufferSizes = (String) msg.obj;
- handleLinkPropertiesUpdate(NO_CALLBACKS);
- break;
-
- case CMD_UPDATE_HTTP_PROXY:
- mHttpProxy = (ProxyInfo) msg.obj;
- handleLinkPropertiesUpdate(NO_CALLBACKS);
- break;
-
- case CMD_SET_MULTICAST_FILTER:
- mMulticastFiltering = (boolean) msg.obj;
- break;
-
- case DhcpClient.CMD_ON_QUIT:
- // Everything is already stopped.
- logError("Unexpected CMD_ON_QUIT (already stopped).");
- break;
-
- default:
- return NOT_HANDLED;
- }
-
- mMsgStateLogger.handled(this, getCurrentState());
- return HANDLED;
- }
- }
-
- class StoppingState extends State {
- @Override
- public void enter() {
- if (mDhcpClient == null) {
- // There's no DHCPv4 for which to wait; proceed to stopped.
- deferMessage(obtainMessage(CMD_JUMP_STOPPING_TO_STOPPED));
- }
- }
-
- @Override
- public boolean processMessage(Message msg) {
- switch (msg.what) {
- case CMD_JUMP_STOPPING_TO_STOPPED:
- transitionTo(mStoppedState);
- break;
-
- case CMD_STOP:
- break;
-
- case DhcpClient.CMD_CLEAR_LINKADDRESS:
- mInterfaceCtrl.clearIPv4Address();
- break;
-
- case DhcpClient.CMD_ON_QUIT:
- mDhcpClient = null;
- transitionTo(mStoppedState);
- break;
-
- default:
- deferMessage(msg);
- }
-
- mMsgStateLogger.handled(this, getCurrentState());
- return HANDLED;
- }
- }
-
- class StartedState extends State {
- @Override
- public void enter() {
- mStartTimeMillis = SystemClock.elapsedRealtime();
-
- if (mConfiguration.mProvisioningTimeoutMs > 0) {
- final long alarmTime = SystemClock.elapsedRealtime()
- + mConfiguration.mProvisioningTimeoutMs;
- mProvisioningTimeoutAlarm.schedule(alarmTime);
- }
-
- if (readyToProceed()) {
- deferMessage(obtainMessage(CMD_JUMP_STARTED_TO_RUNNING));
- } else {
- // Clear all IPv4 and IPv6 before proceeding to RunningState.
- // Clean up any leftover state from an abnormal exit from
- // tethering or during an IpClient restart.
- stopAllIP();
- }
- }
-
- @Override
- public void exit() {
- mProvisioningTimeoutAlarm.cancel();
- }
-
- @Override
- public boolean processMessage(Message msg) {
- switch (msg.what) {
- case CMD_JUMP_STARTED_TO_RUNNING:
- transitionTo(mRunningState);
- break;
-
- case CMD_STOP:
- transitionTo(mStoppingState);
- break;
-
- case EVENT_NETLINK_LINKPROPERTIES_CHANGED:
- handleLinkPropertiesUpdate(NO_CALLBACKS);
- if (readyToProceed()) {
- transitionTo(mRunningState);
- }
- break;
-
- case EVENT_PROVISIONING_TIMEOUT:
- handleProvisioningFailure();
- break;
-
- default:
- // It's safe to process messages out of order because the
- // only message that can both
- // a) be received at this time and
- // b) affect provisioning state
- // is EVENT_NETLINK_LINKPROPERTIES_CHANGED (handled above).
- deferMessage(msg);
- }
-
- mMsgStateLogger.handled(this, getCurrentState());
- return HANDLED;
- }
-
- private boolean readyToProceed() {
- return (!mLinkProperties.hasIPv4Address() && !mLinkProperties.hasGlobalIPv6Address());
- }
- }
-
- class RunningState extends State {
- private ConnectivityPacketTracker mPacketTracker;
- private boolean mDhcpActionInFlight;
-
- @Override
- public void enter() {
- ApfFilter.ApfConfiguration apfConfig = new ApfFilter.ApfConfiguration();
- apfConfig.apfCapabilities = mConfiguration.mApfCapabilities;
- apfConfig.multicastFilter = mMulticastFiltering;
- // Get the Configuration for ApfFilter from Context
- apfConfig.ieee802_3Filter =
- mContext.getResources().getBoolean(R.bool.config_apfDrop802_3Frames);
- apfConfig.ethTypeBlackList =
- mContext.getResources().getIntArray(R.array.config_apfEthTypeBlackList);
- mApfFilter = ApfFilter.maybeCreate(mContext, apfConfig, mInterfaceParams, mCallback);
- // TODO: investigate the effects of any multicast filtering racing/interfering with the
- // rest of this IP configuration startup.
- if (mApfFilter == null) {
- mCallback.setFallbackMulticastFilter(mMulticastFiltering);
- }
-
- mPacketTracker = createPacketTracker();
- if (mPacketTracker != null) mPacketTracker.start(mConfiguration.mDisplayName);
-
- if (mConfiguration.mEnableIPv6 && !startIPv6()) {
- doImmediateProvisioningFailure(IpManagerEvent.ERROR_STARTING_IPV6);
- enqueueJumpToStoppingState();
- return;
- }
-
- if (mConfiguration.mEnableIPv4 && !startIPv4()) {
- doImmediateProvisioningFailure(IpManagerEvent.ERROR_STARTING_IPV4);
- enqueueJumpToStoppingState();
- return;
- }
-
- final InitialConfiguration config = mConfiguration.mInitialConfig;
- if ((config != null) && !applyInitialConfig(config)) {
- // TODO introduce a new IpManagerEvent constant to distinguish this error case.
- doImmediateProvisioningFailure(IpManagerEvent.ERROR_INVALID_PROVISIONING);
- enqueueJumpToStoppingState();
- return;
- }
-
- if (mConfiguration.mUsingIpReachabilityMonitor && !startIpReachabilityMonitor()) {
- doImmediateProvisioningFailure(
- IpManagerEvent.ERROR_STARTING_IPREACHABILITYMONITOR);
- enqueueJumpToStoppingState();
- return;
- }
- }
-
- @Override
- public void exit() {
- stopDhcpAction();
-
- if (mIpReachabilityMonitor != null) {
- mIpReachabilityMonitor.stop();
- mIpReachabilityMonitor = null;
- }
-
- if (mDhcpClient != null) {
- mDhcpClient.sendMessage(DhcpClient.CMD_STOP_DHCP);
- mDhcpClient.doQuit();
- }
-
- if (mPacketTracker != null) {
- mPacketTracker.stop();
- mPacketTracker = null;
- }
-
- if (mApfFilter != null) {
- mApfFilter.shutdown();
- mApfFilter = null;
- }
-
- resetLinkProperties();
- }
-
- private void enqueueJumpToStoppingState() {
- deferMessage(obtainMessage(CMD_JUMP_RUNNING_TO_STOPPING));
- }
-
- private ConnectivityPacketTracker createPacketTracker() {
- try {
- return new ConnectivityPacketTracker(
- getHandler(), mInterfaceParams, mConnectivityPacketLog);
- } catch (IllegalArgumentException e) {
- return null;
- }
- }
-
- private void ensureDhcpAction() {
- if (!mDhcpActionInFlight) {
- mCallback.onPreDhcpAction();
- mDhcpActionInFlight = true;
- final long alarmTime = SystemClock.elapsedRealtime()
- + mConfiguration.mRequestedPreDhcpActionMs;
- mDhcpActionTimeoutAlarm.schedule(alarmTime);
- }
- }
-
- private void stopDhcpAction() {
- mDhcpActionTimeoutAlarm.cancel();
- if (mDhcpActionInFlight) {
- mCallback.onPostDhcpAction();
- mDhcpActionInFlight = false;
- }
- }
-
- @Override
- public boolean processMessage(Message msg) {
- switch (msg.what) {
- case CMD_JUMP_RUNNING_TO_STOPPING:
- case CMD_STOP:
- transitionTo(mStoppingState);
- break;
-
- case CMD_START:
- logError("ALERT: START received in StartedState. Please fix caller.");
- break;
-
- case CMD_CONFIRM:
- // TODO: Possibly introduce a second type of confirmation
- // that both probes (a) on-link neighbors and (b) does
- // a DHCPv4 RENEW. We used to do this on Wi-Fi framework
- // roams.
- if (mIpReachabilityMonitor != null) {
- mIpReachabilityMonitor.probeAll();
- }
- break;
-
- case EVENT_PRE_DHCP_ACTION_COMPLETE:
- // It's possible to reach here if, for example, someone
- // calls completedPreDhcpAction() after provisioning with
- // a static IP configuration.
- if (mDhcpClient != null) {
- mDhcpClient.sendMessage(DhcpClient.CMD_PRE_DHCP_ACTION_COMPLETE);
- }
- break;
-
- case EVENT_NETLINK_LINKPROPERTIES_CHANGED:
- if (!handleLinkPropertiesUpdate(SEND_CALLBACKS)) {
- transitionTo(mStoppingState);
- }
- break;
-
- case CMD_UPDATE_TCP_BUFFER_SIZES:
- mTcpBufferSizes = (String) msg.obj;
- // This cannot possibly change provisioning state.
- handleLinkPropertiesUpdate(SEND_CALLBACKS);
- break;
-
- case CMD_UPDATE_HTTP_PROXY:
- mHttpProxy = (ProxyInfo) msg.obj;
- // This cannot possibly change provisioning state.
- handleLinkPropertiesUpdate(SEND_CALLBACKS);
- break;
-
- case CMD_SET_MULTICAST_FILTER: {
- mMulticastFiltering = (boolean) msg.obj;
- if (mApfFilter != null) {
- mApfFilter.setMulticastFilter(mMulticastFiltering);
- } else {
- mCallback.setFallbackMulticastFilter(mMulticastFiltering);
- }
- break;
- }
-
- case EVENT_READ_PACKET_FILTER_COMPLETE: {
- if (mApfFilter != null) {
- mApfFilter.setDataSnapshot((byte[]) msg.obj);
- }
- mApfDataSnapshotComplete.open();
- break;
- }
-
- case EVENT_DHCPACTION_TIMEOUT:
- stopDhcpAction();
- break;
-
- case DhcpClient.CMD_PRE_DHCP_ACTION:
- if (mConfiguration.mRequestedPreDhcpActionMs > 0) {
- ensureDhcpAction();
- } else {
- sendMessage(EVENT_PRE_DHCP_ACTION_COMPLETE);
- }
- break;
-
- case DhcpClient.CMD_CLEAR_LINKADDRESS:
- mInterfaceCtrl.clearIPv4Address();
- break;
-
- case DhcpClient.CMD_CONFIGURE_LINKADDRESS: {
- final LinkAddress ipAddress = (LinkAddress) msg.obj;
- if (mInterfaceCtrl.setIPv4Address(ipAddress)) {
- mDhcpClient.sendMessage(DhcpClient.EVENT_LINKADDRESS_CONFIGURED);
- } else {
- logError("Failed to set IPv4 address.");
- dispatchCallback(PROV_CHANGE_LOST_PROVISIONING,
- new LinkProperties(mLinkProperties));
- transitionTo(mStoppingState);
- }
- break;
- }
-
- // This message is only received when:
- //
- // a) initial address acquisition succeeds,
- // b) renew succeeds or is NAK'd,
- // c) rebind succeeds or is NAK'd, or
- // c) the lease expires,
- //
- // but never when initial address acquisition fails. The latter
- // condition is now governed by the provisioning timeout.
- case DhcpClient.CMD_POST_DHCP_ACTION:
- stopDhcpAction();
-
- switch (msg.arg1) {
- case DhcpClient.DHCP_SUCCESS:
- handleIPv4Success((DhcpResults) msg.obj);
- break;
- case DhcpClient.DHCP_FAILURE:
- handleIPv4Failure();
- break;
- default:
- logError("Unknown CMD_POST_DHCP_ACTION status: %s", msg.arg1);
- }
- break;
-
- case DhcpClient.CMD_ON_QUIT:
- // DHCPv4 quit early for some reason.
- logError("Unexpected CMD_ON_QUIT.");
- mDhcpClient = null;
- break;
-
- default:
- return NOT_HANDLED;
- }
-
- mMsgStateLogger.handled(this, getCurrentState());
- return HANDLED;
- }
- }
-
- private static class MessageHandlingLogger {
- public String processedInState;
- public String receivedInState;
-
- public void reset() {
- processedInState = null;
- receivedInState = null;
- }
-
- public void handled(State processedIn, IState receivedIn) {
- processedInState = processedIn.getClass().getSimpleName();
- receivedInState = receivedIn.getName();
- }
-
- public String toString() {
- return String.format("rcvd_in=%s, proc_in=%s",
- receivedInState, processedInState);
- }
- }
-
- private static void setNeighborParameters(
- INetd netd, String ifName, int numSolicits, int interSolicitIntervalMs)
- throws RemoteException, IllegalArgumentException {
- Preconditions.checkNotNull(netd);
- Preconditions.checkArgument(!TextUtils.isEmpty(ifName));
- Preconditions.checkArgument(numSolicits > 0);
- Preconditions.checkArgument(interSolicitIntervalMs > 0);
-
- for (int family : new Integer[]{INetd.IPV4, INetd.IPV6}) {
- netd.setProcSysNet(family, INetd.NEIGH, ifName, "retrans_time_ms",
- Integer.toString(interSolicitIntervalMs));
- netd.setProcSysNet(family, INetd.NEIGH, ifName, "ucast_solicit",
- Integer.toString(numSolicits));
- }
- }
-
- // TODO: extract out into CollectionUtils.
- static <T> boolean any(Iterable<T> coll, Predicate<T> fn) {
- for (T t : coll) {
- if (fn.test(t)) {
- return true;
- }
- }
- return false;
- }
-
- static <T> boolean all(Iterable<T> coll, Predicate<T> fn) {
- return !any(coll, not(fn));
- }
-
- static <T> Predicate<T> not(Predicate<T> fn) {
- return (t) -> !fn.test(t);
- }
-
- static <T> String join(String delimiter, Collection<T> coll) {
- return coll.stream().map(Object::toString).collect(Collectors.joining(delimiter));
- }
-
- static <T> T find(Iterable<T> coll, Predicate<T> fn) {
- for (T t: coll) {
- if (fn.test(t)) {
- return t;
- }
- }
- return null;
- }
-
- static <T> List<T> findAll(Collection<T> coll, Predicate<T> fn) {
- return coll.stream().filter(fn).collect(Collectors.toList());
- }
}
diff --git a/services/net/java/android/net/ip/IpClientUtil.java b/services/net/java/android/net/ip/IpClientUtil.java
index 0aec101..2a2a67a 100644
--- a/services/net/java/android/net/ip/IpClientUtil.java
+++ b/services/net/java/android/net/ip/IpClientUtil.java
@@ -16,8 +16,15 @@
package android.net.ip;
+import static android.net.shared.IpConfigurationParcelableUtil.fromStableParcelable;
+import static android.net.shared.LinkPropertiesParcelableUtil.fromStableParcelable;
+
import android.content.Context;
+import android.net.DhcpResultsParcelable;
import android.net.LinkProperties;
+import android.net.LinkPropertiesParcelable;
+import android.net.NetworkStack;
+import android.net.ip.IIpClientCallbacks;
import android.os.ConditionVariable;
import java.io.FileDescriptor;
@@ -31,8 +38,8 @@
* @hide
*/
public class IpClientUtil {
- // TODO: remove once IpClient dumps are moved to NetworkStack and callers don't need this arg
- public static final String DUMP_ARG = IpClient.DUMP_ARG;
+ // TODO: remove with its callers
+ public static final String DUMP_ARG = "ipclient";
/**
* Subclass of {@link IpClientCallbacks} allowing clients to block until provisioning is
@@ -69,24 +76,129 @@
*
* <p>This is a convenience method to allow clients to use {@link IpClientCallbacks} instead of
* {@link IIpClientCallbacks}.
+ * @see {@link NetworkStack#makeIpClient(String, IIpClientCallbacks)}
*/
public static void makeIpClient(Context context, String ifName, IpClientCallbacks callback) {
- // TODO: request IpClient asynchronously from NetworkStack.
- final IpClient ipClient = new IpClient(context, ifName, callback);
- callback.onIpClientCreated(ipClient.makeConnector());
+ context.getSystemService(NetworkStack.class)
+ .makeIpClient(ifName, new IpClientCallbacksProxy(callback));
+ }
+
+ /**
+ * Create a new IpClient.
+ *
+ * <p>This is a convenience method to allow clients to use {@link IpClientCallbacksProxy}
+ * instead of {@link IIpClientCallbacks}.
+ * @see {@link NetworkStack#makeIpClient(String, IIpClientCallbacks)}
+ */
+ public static void makeIpClient(
+ Context context, String ifName, IpClientCallbacksProxy callback) {
+ context.getSystemService(NetworkStack.class)
+ .makeIpClient(ifName, callback);
+ }
+
+ /**
+ * Wrapper to relay calls from {@link IIpClientCallbacks} to {@link IpClientCallbacks}.
+ */
+ public static class IpClientCallbacksProxy extends IIpClientCallbacks.Stub {
+ protected final IpClientCallbacks mCb;
+
+ /**
+ * Create a new IpClientCallbacksProxy.
+ */
+ public IpClientCallbacksProxy(IpClientCallbacks cb) {
+ mCb = cb;
+ }
+
+ @Override
+ public void onIpClientCreated(IIpClient ipClient) {
+ mCb.onIpClientCreated(ipClient);
+ }
+
+ @Override
+ public void onPreDhcpAction() {
+ mCb.onPreDhcpAction();
+ }
+
+ @Override
+ public void onPostDhcpAction() {
+ mCb.onPostDhcpAction();
+ }
+
+ // This is purely advisory and not an indication of provisioning
+ // success or failure. This is only here for callers that want to
+ // expose DHCPv4 results to other APIs (e.g., WifiInfo#setInetAddress).
+ // DHCPv4 or static IPv4 configuration failure or success can be
+ // determined by whether or not the passed-in DhcpResults object is
+ // null or not.
+ @Override
+ public void onNewDhcpResults(DhcpResultsParcelable dhcpResults) {
+ mCb.onNewDhcpResults(fromStableParcelable(dhcpResults));
+ }
+
+ @Override
+ public void onProvisioningSuccess(LinkPropertiesParcelable newLp) {
+ mCb.onProvisioningSuccess(fromStableParcelable(newLp));
+ }
+ @Override
+ public void onProvisioningFailure(LinkPropertiesParcelable newLp) {
+ mCb.onProvisioningFailure(fromStableParcelable(newLp));
+ }
+
+ // Invoked on LinkProperties changes.
+ @Override
+ public void onLinkPropertiesChange(LinkPropertiesParcelable newLp) {
+ mCb.onLinkPropertiesChange(fromStableParcelable(newLp));
+ }
+
+ // Called when the internal IpReachabilityMonitor (if enabled) has
+ // detected the loss of a critical number of required neighbors.
+ @Override
+ public void onReachabilityLost(String logMsg) {
+ mCb.onReachabilityLost(logMsg);
+ }
+
+ // Called when the IpClient state machine terminates.
+ @Override
+ public void onQuit() {
+ mCb.onQuit();
+ }
+
+ // Install an APF program to filter incoming packets.
+ @Override
+ public void installPacketFilter(byte[] filter) {
+ mCb.installPacketFilter(filter);
+ }
+
+ // Asynchronously read back the APF program & data buffer from the wifi driver.
+ // Due to Wifi HAL limitations, the current implementation only supports dumping the entire
+ // buffer. In response to this request, the driver returns the data buffer asynchronously
+ // by sending an IpClient#EVENT_READ_PACKET_FILTER_COMPLETE message.
+ @Override
+ public void startReadPacketFilter() {
+ mCb.startReadPacketFilter();
+ }
+
+ // If multicast filtering cannot be accomplished with APF, this function will be called to
+ // actuate multicast filtering using another means.
+ @Override
+ public void setFallbackMulticastFilter(boolean enabled) {
+ mCb.setFallbackMulticastFilter(enabled);
+ }
+
+ // Enabled/disable Neighbor Discover offload functionality. This is
+ // called, for example, whenever 464xlat is being started or stopped.
+ @Override
+ public void setNeighborDiscoveryOffload(boolean enable) {
+ mCb.setNeighborDiscoveryOffload(enable);
+ }
}
/**
* Dump logs for the specified IpClient.
- * TODO: remove logging from this method once IpClient logs are dumped in NetworkStack dumpsys,
- * then remove callers and delete.
+ * TODO: remove callers and delete
*/
public static void dumpIpClient(
IIpClient connector, FileDescriptor fd, PrintWriter pw, String[] args) {
- if (!(connector instanceof IpClient.IpClientConnector)) {
- pw.println("Invalid connector");
- return;
- }
- ((IpClient.IpClientConnector) connector).dumpIpClientLogs(fd, pw, args);
+ pw.println("IpClient logs have moved to dumpsys network_stack");
}
}
diff --git a/services/net/java/android/net/ip/IpServer.java b/services/net/java/android/net/ip/IpServer.java
index 7910c9a..f7360f5 100644
--- a/services/net/java/android/net/ip/IpServer.java
+++ b/services/net/java/android/net/ip/IpServer.java
@@ -40,7 +40,7 @@
import android.net.ip.RouterAdvertisementDaemon.RaParams;
import android.net.util.InterfaceParams;
import android.net.util.InterfaceSet;
-import android.net.util.NetdService;
+import android.net.shared.NetdService;
import android.net.util.SharedLog;
import android.os.INetworkManagementService;
import android.os.Looper;
diff --git a/services/net/java/android/net/util/NetdService.java b/services/net/java/android/net/shared/NetdService.java
similarity index 99%
rename from services/net/java/android/net/util/NetdService.java
rename to services/net/java/android/net/shared/NetdService.java
index 80b2c27..be0f5f2 100644
--- a/services/net/java/android/net/util/NetdService.java
+++ b/services/net/java/android/net/shared/NetdService.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.net.util;
+package android.net.shared;
import android.net.INetd;
import android.os.RemoteException;
diff --git a/services/net/java/android/net/shared/NetworkMonitorUtils.java b/services/net/java/android/net/shared/NetworkMonitorUtils.java
index 463cf2a..3d2a2de 100644
--- a/services/net/java/android/net/shared/NetworkMonitorUtils.java
+++ b/services/net/java/android/net/shared/NetworkMonitorUtils.java
@@ -16,6 +16,11 @@
package android.net.shared;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED;
+
import android.content.Context;
import android.net.NetworkCapabilities;
import android.provider.Settings;
@@ -58,9 +63,12 @@
* @param dfltNetCap Default requested network capabilities.
* @param nc Network capabilities of the network to test.
*/
- public static boolean isValidationRequired(
- NetworkCapabilities dfltNetCap, NetworkCapabilities nc) {
+ public static boolean isValidationRequired(NetworkCapabilities nc) {
// TODO: Consider requiring validation for DUN networks.
- return dfltNetCap.satisfiedByNetworkCapabilities(nc);
+ return nc != null
+ && nc.hasCapability(NET_CAPABILITY_INTERNET)
+ && nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)
+ && nc.hasCapability(NET_CAPABILITY_TRUSTED)
+ && nc.hasCapability(NET_CAPABILITY_NOT_VPN);
}
}
diff --git a/services/net/java/android/net/util/InterfaceParams.java b/services/net/java/android/net/util/InterfaceParams.java
index 7b060da..f6bb873 100644
--- a/services/net/java/android/net/util/InterfaceParams.java
+++ b/services/net/java/android/net/util/InterfaceParams.java
@@ -16,9 +16,6 @@
package android.net.util;
-import static android.net.util.NetworkConstants.ETHER_MTU;
-import static android.net.util.NetworkConstants.IPV6_MIN_MTU;
-
import static com.android.internal.util.Preconditions.checkArgument;
import android.net.MacAddress;
@@ -44,6 +41,11 @@
public final MacAddress macAddr;
public final int defaultMtu;
+ // TODO: move the below to NetworkStackConstants when this class is moved to the NetworkStack.
+ private static final int ETHER_MTU = 1500;
+ private static final int IPV6_MIN_MTU = 1280;
+
+
public static InterfaceParams getByName(String name) {
final NetworkInterface netif = getNetworkInterfaceByName(name);
if (netif == null) return null;
diff --git a/services/net/java/android/net/util/NetworkConstants.java b/services/net/java/android/net/util/NetworkConstants.java
index c183b81..ea5ce65 100644
--- a/services/net/java/android/net/util/NetworkConstants.java
+++ b/services/net/java/android/net/util/NetworkConstants.java
@@ -28,28 +28,6 @@
public final class NetworkConstants {
private NetworkConstants() { throw new RuntimeException("no instance permitted"); }
- /**
- * Ethernet constants.
- *
- * See also:
- * - https://tools.ietf.org/html/rfc894
- * - https://tools.ietf.org/html/rfc2464
- * - https://tools.ietf.org/html/rfc7042
- * - http://www.iana.org/assignments/ethernet-numbers/ethernet-numbers.xhtml
- * - http://www.iana.org/assignments/ieee-802-numbers/ieee-802-numbers.xhtml
- */
- public static final int ETHER_DST_ADDR_OFFSET = 0;
- public static final int ETHER_SRC_ADDR_OFFSET = 6;
- public static final int ETHER_ADDR_LEN = 6;
-
- public static final int ETHER_TYPE_OFFSET = 12;
- public static final int ETHER_TYPE_LENGTH = 2;
- public static final int ETHER_TYPE_ARP = 0x0806;
- public static final int ETHER_TYPE_IPV4 = 0x0800;
- public static final int ETHER_TYPE_IPV6 = 0x86dd;
-
- public static final int ETHER_HEADER_LEN = 14;
-
public static final byte FF = asByte(0xff);
public static final byte[] ETHER_ADDR_BROADCAST = {
FF, FF, FF, FF, FF, FF
@@ -58,34 +36,12 @@
public static final int ETHER_MTU = 1500;
/**
- * ARP constants.
- *
- * See also:
- * - https://tools.ietf.org/html/rfc826
- * - http://www.iana.org/assignments/arp-parameters/arp-parameters.xhtml
- */
- public static final int ARP_PAYLOAD_LEN = 28; // For Ethernet+IPv4.
- public static final int ARP_REQUEST = 1;
- public static final int ARP_REPLY = 2;
- public static final int ARP_HWTYPE_RESERVED_LO = 0;
- public static final int ARP_HWTYPE_ETHER = 1;
- public static final int ARP_HWTYPE_RESERVED_HI = 0xffff;
-
- /**
* IPv4 constants.
*
* See also:
* - https://tools.ietf.org/html/rfc791
*/
- public static final int IPV4_HEADER_MIN_LEN = 20;
- public static final int IPV4_IHL_MASK = 0xf;
- public static final int IPV4_FLAGS_OFFSET = 6;
- public static final int IPV4_FRAGMENT_MASK = 0x1fff;
- public static final int IPV4_PROTOCOL_OFFSET = 9;
- public static final int IPV4_SRC_ADDR_OFFSET = 12;
- public static final int IPV4_DST_ADDR_OFFSET = 16;
public static final int IPV4_ADDR_BITS = 32;
- public static final int IPV4_ADDR_LEN = 4;
/**
* IPv6 constants.
@@ -93,15 +49,10 @@
* See also:
* - https://tools.ietf.org/html/rfc2460
*/
- public static final int IPV6_HEADER_LEN = 40;
- public static final int IPV6_PROTOCOL_OFFSET = 6;
- public static final int IPV6_SRC_ADDR_OFFSET = 8;
- public static final int IPV6_DST_ADDR_OFFSET = 24;
public static final int IPV6_ADDR_BITS = 128;
public static final int IPV6_ADDR_LEN = 16;
public static final int IPV6_MIN_MTU = 1280;
public static final int RFC7421_PREFIX_LENGTH = 64;
- public static final int RFC6177_MIN_PREFIX_LENGTH = 48;
/**
* ICMP common (v4/v6) constants.
@@ -124,45 +75,7 @@
* - https://tools.ietf.org/html/rfc792
*/
public static final int ICMPV4_ECHO_REQUEST_TYPE = 8;
-
- /**
- * ICMPv6 constants.
- *
- * See also:
- * - https://tools.ietf.org/html/rfc4443
- * - https://tools.ietf.org/html/rfc4861
- */
- public static final int ICMPV6_HEADER_MIN_LEN = 4;
public static final int ICMPV6_ECHO_REQUEST_TYPE = 128;
- public static final int ICMPV6_ECHO_REPLY_TYPE = 129;
- public static final int ICMPV6_ROUTER_SOLICITATION = 133;
- public static final int ICMPV6_ROUTER_ADVERTISEMENT = 134;
- public static final int ICMPV6_NEIGHBOR_SOLICITATION = 135;
- public static final int ICMPV6_NEIGHBOR_ADVERTISEMENT = 136;
-
- public static final int ICMPV6_ND_OPTION_MIN_LENGTH = 8;
- public static final int ICMPV6_ND_OPTION_LENGTH_SCALING_FACTOR = 8;
- public static final int ICMPV6_ND_OPTION_SLLA = 1;
- public static final int ICMPV6_ND_OPTION_TLLA = 2;
- public static final int ICMPV6_ND_OPTION_MTU = 5;
-
-
- /**
- * UDP constants.
- *
- * See also:
- * - https://tools.ietf.org/html/rfc768
- */
- public static final int UDP_HEADER_LEN = 8;
-
- /**
- * DHCP(v4) constants.
- *
- * See also:
- * - https://tools.ietf.org/html/rfc2131
- */
- public static final int DHCP4_SERVER_PORT = 67;
- public static final int DHCP4_CLIENT_PORT = 68;
/**
* DNS constants.
@@ -176,9 +89,4 @@
* Utility functions.
*/
public static byte asByte(int i) { return (byte) i; }
-
- public static String asString(int i) { return Integer.toString(i); }
-
- public static int asUint(byte b) { return (b & 0xff); }
- public static int asUint(short s) { return (s & 0xffff); }
}
diff --git a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
index 4811523..164570a 100644
--- a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
@@ -116,6 +116,7 @@
import com.android.server.backup.testing.TransportTestUtils.TransportMock;
import com.android.server.testing.shadows.FrameworkShadowLooper;
import com.android.server.testing.shadows.ShadowApplicationPackageManager;
+import com.android.server.testing.shadows.ShadowBackupActivityThread;
import com.android.server.testing.shadows.ShadowBackupDataInput;
import com.android.server.testing.shadows.ShadowBackupDataOutput;
import com.android.server.testing.shadows.ShadowEventLog;
@@ -163,6 +164,7 @@
ShadowBackupDataOutput.class,
ShadowEventLog.class,
ShadowQueuedWork.class,
+ ShadowBackupActivityThread.class,
})
@Presubmit
public class KeyValueBackupTaskTest {
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowAppBackupUtils.java b/services/robotests/src/com/android/server/testing/shadows/ShadowAppBackupUtils.java
index aefc871..33b8aa7 100644
--- a/services/robotests/src/com/android/server/testing/shadows/ShadowAppBackupUtils.java
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowAppBackupUtils.java
@@ -62,7 +62,7 @@
}
@Implementation
- protected static boolean appIsEligibleForBackup(ApplicationInfo app, PackageManager pm) {
+ protected static boolean appIsEligibleForBackup(ApplicationInfo app, int userId) {
return sAppsEligibleForBackup.contains(app.packageName);
}
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowBackupActivityThread.java b/services/robotests/src/com/android/server/testing/shadows/ShadowBackupActivityThread.java
new file mode 100644
index 0000000..ca2e3b6
--- /dev/null
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowBackupActivityThread.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.testing.shadows;
+
+import android.app.ActivityThread;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.os.RemoteException;
+
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.shadows.ShadowActivityThread;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+import javax.annotation.Nonnull;
+
+/**
+ * Extends the existing {@link ShadowActivityThread} to add support for
+ * {@link PackageManager#getApplicationEnabledSetting(String)} in the shadow {@link PackageManager}
+ * returned by {@link ShadowBackupActivityThread#getPackageManager()}.
+ */
+@Implements(value = ActivityThread.class, isInAndroidSdk = false, looseSignatures = true)
+public class ShadowBackupActivityThread extends ShadowActivityThread {
+ @Implementation
+ public static Object getPackageManager() {
+ ClassLoader classLoader = ShadowActivityThread.class.getClassLoader();
+ Class<?> iPackageManagerClass;
+ try {
+ iPackageManagerClass = classLoader.loadClass("android.content.pm.IPackageManager");
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+
+ return Proxy.newProxyInstance(
+ classLoader,
+ new Class[] {iPackageManagerClass},
+ new InvocationHandler() {
+ @Override
+ public Object invoke(Object proxy, @Nonnull Method method, Object[] args)
+ throws Exception {
+ if (method.getName().equals("getApplicationInfo")) {
+ String packageName = (String) args[0];
+ int flags = (Integer) args[1];
+
+ try {
+ return RuntimeEnvironment.application
+ .getPackageManager()
+ .getApplicationInfo(packageName, flags);
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new RemoteException(e.getMessage());
+ }
+ } else if (method.getName().equals("getApplicationEnabledSetting")) {
+ return 0;
+ } else {
+ return null;
+ }
+ }
+ });
+ }
+}
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 1b5ba26..dc31c0f 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -156,6 +156,7 @@
</activity>
<activity android:name="com.android.server.accounts.AccountAuthenticatorDummyActivity" />
+ <activity android:name="com.android.server.adb.AdbDebuggingManagerTestActivity" />
<activity-alias android:name="a.ShortcutEnabled"
android:targetActivity="com.android.server.pm.ShortcutTestActivity"
diff --git a/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java b/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java
new file mode 100644
index 0000000..65af677
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java
@@ -0,0 +1,497 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.adb;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.content.Context;
+import android.provider.Settings;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.android.server.FgThread;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.io.File;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(JUnit4.class)
+public final class AdbDebuggingManagerTest {
+
+ private static final String TAG = "AdbDebuggingManagerTest";
+
+ // This component is passed to the AdbDebuggingManager to act as the activity that can confirm
+ // unknown adb keys. An overlay package was first attempted to override the
+ // config_customAdbPublicKeyConfirmationComponent config, but the value from that package was
+ // not being read.
+ private static final String ADB_CONFIRM_COMPONENT =
+ "com.android.frameworks.servicestests/"
+ + "com.android.server.adb.AdbDebuggingManagerTestActivity";
+
+ // The base64 encoding of the values 'test key 1' and 'test key 2'.
+ private static final String TEST_KEY_1 = "dGVzdCBrZXkgMQo=";
+ private static final String TEST_KEY_2 = "dGVzdCBrZXkgMgo=";
+
+ // This response is received from the AdbDebuggingHandler when the key is allowed to connect
+ private static final String RESPONSE_KEY_ALLOWED = "OK";
+ // This response is received from the AdbDebuggingHandler when the key is not allowed to connect
+ private static final String RESPONSE_KEY_DENIED = "NO";
+
+ // wait up to 5 seconds for any blocking queries
+ private static final long TIMEOUT = 5000;
+ private static final TimeUnit TIMEOUT_TIME_UNIT = TimeUnit.MILLISECONDS;
+
+ private Context mContext;
+ private AdbDebuggingManager mManager;
+ private AdbDebuggingManager.AdbDebuggingThread mThread;
+ private AdbDebuggingManager.AdbDebuggingHandler mHandler;
+ private AdbDebuggingManager.AdbKeyStore mKeyStore;
+ private BlockingQueue<TestResult> mBlockingQueue;
+ private long mOriginalAllowedConnectionTime;
+ private File mKeyFile;
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getContext();
+ mManager = new AdbDebuggingManager(mContext, ADB_CONFIRM_COMPONENT);
+ mKeyFile = new File(mContext.getFilesDir(), "test_adb_keys.xml");
+ if (mKeyFile.exists()) {
+ mKeyFile.delete();
+ }
+ mThread = new AdbDebuggingThreadTest();
+ mKeyStore = mManager.new AdbKeyStore(mKeyFile);
+ mHandler = mManager.new AdbDebuggingHandler(FgThread.get().getLooper(), mThread, mKeyStore);
+ mOriginalAllowedConnectionTime = mKeyStore.getAllowedConnectionTime();
+ mBlockingQueue = new ArrayBlockingQueue<>(1);
+
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mKeyStore.deleteKeyStore();
+ setAllowedConnectionTime(mOriginalAllowedConnectionTime);
+ }
+
+ /**
+ * Sets the allowed connection time within which a subsequent connection from a key for which
+ * the user selected the 'Always allow' option will be allowed without user interaction.
+ */
+ private void setAllowedConnectionTime(long connectionTime) {
+ Settings.Global.putLong(mContext.getContentResolver(),
+ Settings.Global.ADB_ALLOWED_CONNECTION_TIME, connectionTime);
+ };
+
+ @Test
+ public void testAllowNewKeyOnce() throws Exception {
+ // Verifies the behavior when a new key first attempts to connect to a device. During the
+ // first connection the ADB confirmation activity should be launched to prompt the user to
+ // allow the connection with an option to always allow connections from this key.
+
+ // Verify if the user allows the key but does not select the option to 'always
+ // allow' that the connection is allowed but the key is not stored.
+ runAdbTest(TEST_KEY_1, true, false, false);
+ }
+
+ @Test
+ public void testDenyNewKey() throws Exception {
+ // Verifies if the user does not allow the key then the connection is not allowed and the
+ // key is not stored.
+ runAdbTest(TEST_KEY_1, false, false, false);
+ }
+
+ @Test
+ public void testDisconnectAlwaysAllowKey() throws Exception {
+ // When a key is disconnected from a device ADB should send a disconnect message; this
+ // message should trigger an update of the last connection time for the currently connected
+ // key.
+
+ // Allow a connection from a new key with the 'Always allow' option selected.
+ runAdbTest(TEST_KEY_1, true, true, false);
+
+ // Get the last connection time for the currently connected key to verify that it is updated
+ // after the disconnect.
+ long lastConnectionTime = mKeyStore.getLastConnectionTime(TEST_KEY_1);
+
+ // Sleep for a small amount of time to ensure a difference can be observed in the last
+ // connection time after a disconnect.
+ Thread.sleep(10);
+
+ // Send the disconnect message for the currently connected key to trigger an update of the
+ // last connection time.
+ mHandler.obtainMessage(
+ AdbDebuggingManager.AdbDebuggingHandler.MESSAGE_ADB_DISCONNECT).sendToTarget();
+
+ // Use a latch to ensure the test does not exit untill the Runnable has been processed.
+ CountDownLatch latch = new CountDownLatch(1);
+
+ // Post a new Runnable to the handler to ensure it runs after the disconnect message is
+ // processed.
+ mHandler.post(() -> {
+ assertNotEquals(
+ "The last connection time was not updated after the disconnect",
+ lastConnectionTime,
+ mKeyStore.getLastConnectionTime(TEST_KEY_1));
+ latch.countDown();
+ });
+ if (!latch.await(TIMEOUT, TIMEOUT_TIME_UNIT)) {
+ fail("The Runnable to verify the last connection time was updated did not complete "
+ + "within the timeout period");
+ }
+ }
+
+ @Test
+ public void testDisconnectAllowedOnceKey() throws Exception {
+ // When a key is disconnected ADB should send a disconnect message; this message should
+ // essentially result in a noop for keys that the user only allows once since the last
+ // connection time is not maintained for these keys.
+
+ // Allow a connection from a new key with the 'Always allow' option set to false
+ runAdbTest(TEST_KEY_1, true, false, false);
+
+ // Send the disconnect message for the currently connected key.
+ mHandler.obtainMessage(
+ AdbDebuggingManager.AdbDebuggingHandler.MESSAGE_ADB_DISCONNECT).sendToTarget();
+
+ // Verify that the disconnected key is not automatically allowed on a subsequent connection.
+ runAdbTest(TEST_KEY_1, true, false, false);
+ }
+
+ @Test
+ public void testAlwaysAllowConnectionFromKey() throws Exception {
+ // Verifies when the user selects the 'Always allow' option for the current key that
+ // subsequent connection attempts from that key are allowed.
+
+ // Allow a connection from a new key with the 'Always allow' option selected.
+ runAdbTest(TEST_KEY_1, true, true, false);
+
+ // Next attempt another connection with the same key and verify that the activity to prompt
+ // the user to accept the key is not launched.
+ runAdbTest(TEST_KEY_1, true, true, true);
+
+ // Verify that a different key is not automatically allowed.
+ runAdbTest(TEST_KEY_2, false, false, false);
+ }
+
+ @Test
+ public void testOriginalAlwaysAllowBehavior() throws Exception {
+ // If the Settings.Global.ADB_ALLOWED_CONNECTION_TIME setting is set to 0 then the original
+ // behavior of 'Always allow' should be restored.
+
+ // Accept the test key with the 'Always allow' option selected.
+ runAdbTest(TEST_KEY_1, true, true, false);
+
+ // Set the connection time to 0 to restore the original behavior.
+ setAllowedConnectionTime(0);
+
+ // Set the last connection time to the test key to a very small value to ensure it would
+ // fail the new test but would be allowed with the original behavior.
+ mKeyStore.setLastConnectionTime(TEST_KEY_1, 1);
+
+ // Run the test with the key and verify that the connection is automatically allowed.
+ runAdbTest(TEST_KEY_1, true, true, true);
+ }
+
+ @Test
+ public void testLastConnectionTimeUpdatedByScheduledJob() throws Exception {
+ // If a development device is left connected to a system beyond the allowed connection time
+ // a reboot of the device while connected could make it appear as though the last connection
+ // time is beyond the allowed window. A scheduled job runs daily while a key is connected
+ // to update the last connection time to the current time; this ensures if the device is
+ // rebooted while connected to a system the last connection time should be within 24 hours.
+
+ // Allow the key to connect with the 'Always allow' option selected
+ runAdbTest(TEST_KEY_1, true, true, false);
+
+ // Get the current last connection time for comparison after the scheduled job is run
+ long lastConnectionTime = mKeyStore.getLastConnectionTime(TEST_KEY_1);
+
+ // Sleep a small amount of time to ensure that the updated connection time changes
+ Thread.sleep(10);
+
+ // Send a message to the handler to update the last connection time for the active key
+ mHandler.obtainMessage(
+ AdbDebuggingManager.AdbDebuggingHandler.MESSAGE_ADB_UPDATE_KEY_CONNECTION_TIME)
+ .sendToTarget();
+
+ // Post a Runnable to the handler to ensure it runs after the update key connection time
+ // message is processed.
+ CountDownLatch latch = new CountDownLatch(1);
+ mHandler.post(() -> {
+ assertNotEquals(
+ "The last connection time of the key was not updated after the update key "
+ + "connection time message",
+ lastConnectionTime, mKeyStore.getLastConnectionTime(TEST_KEY_1));
+ latch.countDown();
+ });
+ if (!latch.await(TIMEOUT, TIMEOUT_TIME_UNIT)) {
+ fail("The Runnable to verify the last connection time was updated did not complete "
+ + "within the timeout period");
+ }
+ }
+
+ @Test
+ public void testKeystorePersisted() throws Exception {
+ // After any updates are made to the key store a message should be sent to persist the
+ // key store. This test verifies that a key that is always allowed is persisted in the key
+ // store along with its last connection time.
+
+ // Allow the key to connect with the 'Always allow' option selected
+ runAdbTest(TEST_KEY_1, true, true, false);
+
+ // Send a message to the handler to persist the updated keystore.
+ mHandler.obtainMessage(
+ AdbDebuggingManager.AdbDebuggingHandler.MESSAGE_ADB_PERSIST_KEY_STORE)
+ .sendToTarget();
+
+ // Post a Runnable to the handler to ensure the persist key store message has been processed
+ // using a new AdbKeyStore backed by the key file.
+ mHandler.post(() -> assertTrue(
+ "The key with the 'Always allow' option selected was not persisted in the keystore",
+ mManager.new AdbKeyStore(mKeyFile).isKeyAuthorized(TEST_KEY_1)));
+
+ // Get the current last connection time to ensure it is updated in the persisted keystore.
+ long lastConnectionTime = mKeyStore.getLastConnectionTime(TEST_KEY_1);
+
+ // Sleep a small amount of time to ensure the last connection time is updated.
+ Thread.sleep(10);
+
+ // Send a message to the handler to update the last connection time for the active key.
+ mHandler.obtainMessage(
+ AdbDebuggingManager.AdbDebuggingHandler.MESSAGE_ADB_UPDATE_KEY_CONNECTION_TIME)
+ .sendToTarget();
+
+ // Persist the updated last connection time.
+ mHandler.obtainMessage(
+ AdbDebuggingManager.AdbDebuggingHandler.MESSAGE_ADB_PERSIST_KEY_STORE)
+ .sendToTarget();
+
+ // Post a Runnable with a new key store backed by the key file to verify that the last
+ // connection time obtained above is different from the persisted updated value.
+ CountDownLatch latch = new CountDownLatch(1);
+ mHandler.post(() -> {
+ assertNotEquals(
+ "The last connection time in the key file was not updated after the update "
+ + "connection time message", lastConnectionTime,
+ mManager.new AdbKeyStore(mKeyFile).getLastConnectionTime(TEST_KEY_1));
+ latch.countDown();
+ });
+ if (!latch.await(TIMEOUT, TIMEOUT_TIME_UNIT)) {
+ fail("The Runnable to verify the last connection time was updated did not complete "
+ + "within the timeout period");
+ }
+ }
+
+ @Test
+ public void testAdbClearRemovesActiveKey() throws Exception {
+ // If the user selects the option to 'Revoke USB debugging authorizations' while an 'Always
+ // allow' key is connected that key should be deleted as well.
+
+ // Allow the key to connect with the 'Always allow' option selected
+ runAdbTest(TEST_KEY_1, true, true, false);
+
+ // Send a message to the handler to clear the adb authorizations.
+ mHandler.obtainMessage(
+ AdbDebuggingManager.AdbDebuggingHandler.MESSAGE_ADB_CLEAR).sendToTarget();
+
+ // Send a message to disconnect the currently connected key
+ mHandler.obtainMessage(
+ AdbDebuggingManager.AdbDebuggingHandler.MESSAGE_ADB_DISCONNECT).sendToTarget();
+
+ // Post a Runnable to ensure the disconnect has completed to verify the 'Always allow' key
+ // that was connected when the clear was sent requires authorization.
+ CountDownLatch latch = new CountDownLatch(1);
+ mHandler.post(() -> {
+ assertFalse(
+ "The currently connected 'always allow' key should not be authorized after an"
+ + " adb"
+ + " clear message.",
+ mKeyStore.isKeyAuthorized(TEST_KEY_1));
+ latch.countDown();
+ });
+ if (!latch.await(TIMEOUT, TIMEOUT_TIME_UNIT)) {
+ fail("The Runnable to verify the key is not authorized did not complete within the "
+ + "timeout period");
+ }
+ }
+
+ @Test
+ public void testAdbGrantRevokedIfLastConnectionBeyondAllowedTime() throws Exception {
+ // If the user selects the 'Always allow' option then subsequent connections from the key
+ // will be allowed as long as the connection is within the allowed window. Once the last
+ // connection time is beyond this window the user should be prompted to allow the key again.
+
+ // Allow the key to connect with the 'Always allow' option selected
+ runAdbTest(TEST_KEY_1, true, true, false);
+
+ // Set the allowed window to a small value to ensure the time is beyond the allowed window.
+ setAllowedConnectionTime(1);
+
+ // Sleep for a small amount of time to exceed the allowed window.
+ Thread.sleep(10);
+
+ // A new connection from this key should prompt the user again.
+ runAdbTest(TEST_KEY_1, true, true, false);
+ }
+
+ @Test
+ public void testLastConnectionTimeCannotBeSetBack() throws Exception {
+ // When a device is first booted there is a possibility that the system time will be set to
+ // the build time of the system image. If a device is connected to a system during a reboot
+ // this could cause the connection time to be set in the past; if the device time is not
+ // corrected before the device is disconnected then a subsequent connection with the time
+ // corrected would appear as though the last connection time was beyond the allowed window,
+ // and the user would be required to authorize the connection again. This test verifies that
+ // the AdbKeyStore does not update the last connection time if it is less than the
+ // previously written connection time.
+
+ // Allow the key to connect with the 'Always allow' option selected
+ runAdbTest(TEST_KEY_1, true, true, false);
+
+ // Get the last connection time that was written to the key store.
+ long lastConnectionTime = mKeyStore.getLastConnectionTime(TEST_KEY_1);
+
+ // Attempt to set the last connection time to 1970
+ mKeyStore.setLastConnectionTime(TEST_KEY_1, 0);
+ assertEquals(
+ "The last connection time in the adb key store should not be set to a value less "
+ + "than the previous connection time",
+ lastConnectionTime, mKeyStore.getLastConnectionTime(TEST_KEY_1));
+
+ // Attempt to set the last connection time just beyond the allowed window.
+ mKeyStore.setLastConnectionTime(TEST_KEY_1,
+ Math.max(0, lastConnectionTime - (mKeyStore.getAllowedConnectionTime() + 1)));
+ assertEquals(
+ "The last connection time in the adb key store should not be set to a value less "
+ + "than the previous connection time",
+ lastConnectionTime, mKeyStore.getLastConnectionTime(TEST_KEY_1));
+ }
+
+ /**
+ * Runs an adb test with the provided configuration.
+ *
+ * @param key The base64 encoding of the key to be used during the test.
+ * @param allowKey boolean indicating whether the key should be allowed to connect.
+ * @param alwaysAllow boolean indicating whether the 'Always allow' option should be selected.
+ * @param autoAllowExpected boolean indicating whether the key is expected to be automatically
+ * allowed without user interaction.
+ */
+ private void runAdbTest(String key, boolean allowKey, boolean alwaysAllow,
+ boolean autoAllowExpected) throws Exception {
+ // if the key should not be automatically allowed then set up the activity
+ if (!autoAllowExpected) {
+ new AdbDebuggingManagerTestActivity.Configurator()
+ .setExpectedKey(key)
+ .setAllowKey(allowKey)
+ .setAlwaysAllow(alwaysAllow)
+ .setHandler(mHandler)
+ .setBlockingQueue(mBlockingQueue);
+ }
+ // send the message indicating a new key is attempting to connect
+ mHandler.obtainMessage(AdbDebuggingManager.AdbDebuggingHandler.MESSAGE_ADB_CONFIRM,
+ key).sendToTarget();
+ // if the key should not be automatically allowed then the ADB public key confirmation
+ // activity should be launched
+ if (!autoAllowExpected) {
+ TestResult activityResult = mBlockingQueue.poll(TIMEOUT, TIMEOUT_TIME_UNIT);
+ assertNotNull(
+ "The ADB public key confirmation activity did not complete within the timeout"
+ + " period", activityResult);
+ assertEquals("The ADB public key activity failed with result: " + activityResult,
+ TestResult.RESULT_ACTIVITY_LAUNCHED, activityResult.mReturnCode);
+ }
+ // If the activity was launched it should send a response back to the manager that would
+ // trigger a response to the thread, or if the key is a known valid key then a response
+ // should be sent back without requiring interaction with the activity.
+ TestResult threadResult = mBlockingQueue.poll(TIMEOUT, TIMEOUT_TIME_UNIT);
+ assertNotNull("A response was not sent to the thread within the timeout period",
+ threadResult);
+ // verify that the result is an expected message from the thread
+ assertEquals("An unexpected result was received: " + threadResult,
+ TestResult.RESULT_RESPONSE_RECEIVED, threadResult.mReturnCode);
+ assertEquals("The manager did not send the proper response for allowKey = " + allowKey,
+ allowKey ? RESPONSE_KEY_ALLOWED : RESPONSE_KEY_DENIED, threadResult.mMessage);
+ // if the key is not allowed or not always allowed verify it is not in the key store
+ if (!allowKey || !alwaysAllow) {
+ assertFalse(
+ "The key should not be allowed automatically on subsequent connection attempts",
+ mKeyStore.isKeyAuthorized(key));
+ }
+ }
+
+ /**
+ * Helper class that extends AdbDebuggingThread to receive the response from AdbDebuggingManager
+ * indicating whether the key should be allowed to connect.
+ */
+ class AdbDebuggingThreadTest extends AdbDebuggingManager.AdbDebuggingThread {
+ AdbDebuggingThreadTest() {
+ mManager.super();
+ }
+
+ @Override
+ public void sendResponse(String msg) {
+ TestResult result = new TestResult(TestResult.RESULT_RESPONSE_RECEIVED, msg);
+ try {
+ mBlockingQueue.put(result);
+ } catch (InterruptedException e) {
+ Log.e(TAG,
+ "Caught an InterruptedException putting the result in the queue: " + result,
+ e);
+ }
+ }
+ }
+
+ /**
+ * Contains the result for the current portion of the test along with any corresponding
+ * messages.
+ */
+ public static class TestResult {
+ public int mReturnCode;
+ public String mMessage;
+
+ public static final int RESULT_ACTIVITY_LAUNCHED = 1;
+ public static final int RESULT_UNEXPECTED_KEY = 2;
+ public static final int RESULT_RESPONSE_RECEIVED = 3;
+
+ public TestResult(int returnCode) {
+ this(returnCode, null);
+ }
+
+ public TestResult(int returnCode, String message) {
+ mReturnCode = returnCode;
+ mMessage = message;
+ }
+
+ @Override
+ public String toString() {
+ return "{mReturnCode = " + mReturnCode + ", mMessage = " + mMessage + "}";
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTestActivity.java b/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTestActivity.java
new file mode 100644
index 0000000..1a9c180
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTestActivity.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.adb;
+
+import static com.android.server.adb.AdbDebuggingManagerTest.TestResult;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Message;
+import android.util.Log;
+
+import java.util.concurrent.BlockingQueue;
+
+/**
+ * Helper Activity used to test the AdbDebuggingManager's prompt to allow an adb key.
+ */
+public class AdbDebuggingManagerTestActivity extends Activity {
+
+ private static final String TAG = "AdbDebuggingManagerTestActivity";
+
+ /*
+ * Static values that must be set before each test to modify the behavior of the Activity.
+ */
+ private static AdbDebuggingManager.AdbDebuggingHandler sHandler;
+ private static boolean sAllowKey;
+ private static boolean sAlwaysAllow;
+ private static String sExpectedKey;
+ private static BlockingQueue sBlockingQueue;
+
+ /**
+ * Receives the Intent sent from the AdbDebuggingManager and sends the preconfigured response.
+ */
+ @Override
+ public void onCreate(Bundle bundle) {
+ super.onCreate(bundle);
+ Intent intent = getIntent();
+ String key = intent.getStringExtra("key");
+ if (!key.equals(sExpectedKey)) {
+ TestResult result = new TestResult(TestResult.RESULT_UNEXPECTED_KEY, key);
+ postResult(result);
+ finish();
+ return;
+ }
+ // Post the result that the activity was successfully launched as expected and a response
+ // is being sent to let the test method know that it should move on to waiting for the next
+ // expected response from the AdbDebuggingManager.
+ TestResult result = new TestResult(
+ AdbDebuggingManagerTest.TestResult.RESULT_ACTIVITY_LAUNCHED);
+ postResult(result);
+
+ // Initialize the message based on the preconfigured values. If the key is accepted the
+ // AdbDebuggingManager expects the key to be in the obj field of the message, and if the
+ // user selects the 'Always allow' option the manager expects the arg1 field to be set to 1.
+ int messageType;
+ if (sAllowKey) {
+ messageType = AdbDebuggingManager.AdbDebuggingHandler.MESSAGE_ADB_ALLOW;
+ } else {
+ messageType = AdbDebuggingManager.AdbDebuggingHandler.MESSAGE_ADB_DENY;
+ }
+ Message message = sHandler.obtainMessage(messageType);
+ message.obj = key;
+ if (sAlwaysAllow) {
+ message.arg1 = 1;
+ }
+ finish();
+ sHandler.sendMessage(message);
+ }
+
+ /**
+ * Posts the result of the activity to the test method.
+ */
+ private void postResult(TestResult result) {
+ try {
+ sBlockingQueue.put(result);
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Caught an InterruptedException posting the result " + result, e);
+ }
+ }
+
+ /**
+ * Allows test methods to specify the behavior of the Activity before it is invoked by the
+ * AdbDebuggingManager.
+ */
+ public static class Configurator {
+
+ /**
+ * Sets the test handler to be used by this activity to send the configured response.
+ */
+ public Configurator setHandler(AdbDebuggingManager.AdbDebuggingHandler handler) {
+ sHandler = handler;
+ return this;
+ }
+
+ /**
+ * Sets whether the key should be allowed for this test.
+ */
+ public Configurator setAllowKey(boolean allow) {
+ sAllowKey = allow;
+ return this;
+ }
+
+ /**
+ * Sets whether the 'Always allow' option should be selected for this test.
+ */
+ public Configurator setAlwaysAllow(boolean alwaysAllow) {
+ sAlwaysAllow = alwaysAllow;
+ return this;
+ }
+
+ /**
+ * Sets the key that should be expected from the AdbDebuggingManager for this test.
+ */
+ public Configurator setExpectedKey(String expectedKey) {
+ sExpectedKey = expectedKey;
+ return this;
+ }
+
+ /**
+ * Sets the BlockingQueue that should be used to post the result of the Activity back to the
+ * test method.
+ */
+ public Configurator setBlockingQueue(BlockingQueue blockingQueue) {
+ sBlockingQueue = blockingQueue;
+ return this;
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java b/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java
new file mode 100644
index 0000000..095a1de
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java
@@ -0,0 +1,1172 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.backup.testutils;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.IntentSender;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ChangedPackages;
+import android.content.pm.IDexModuleRegisterCallback;
+import android.content.pm.IOnPermissionsChangeListener;
+import android.content.pm.IPackageDataObserver;
+import android.content.pm.IPackageDeleteObserver;
+import android.content.pm.IPackageDeleteObserver2;
+import android.content.pm.IPackageInstaller;
+import android.content.pm.IPackageManager;
+import android.content.pm.IPackageMoveObserver;
+import android.content.pm.IPackageStatsObserver;
+import android.content.pm.InstrumentationInfo;
+import android.content.pm.KeySet;
+import android.content.pm.ModuleInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ParceledListSlice;
+import android.content.pm.PermissionGroupInfo;
+import android.content.pm.PermissionInfo;
+import android.content.pm.ProviderInfo;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.content.pm.SuspendDialogInfo;
+import android.content.pm.VerifierDeviceIdentity;
+import android.content.pm.VersionedPackage;
+import android.content.pm.dex.IArtManager;
+import android.graphics.Bitmap;
+import android.os.IBinder;
+import android.os.PersistableBundle;
+import android.os.RemoteException;
+import java.util.List;
+
+/**
+ * Stub for IPackageManager to use in tests.
+ */
+public class IPackageManagerStub implements IPackageManager {
+ public static PackageInfo sPackageInfo;
+ public static int sApplicationEnabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
+
+ @Override
+ public PackageInfo getPackageInfo(String packageName, int flags, int userId)
+ throws RemoteException {
+ return sPackageInfo;
+ }
+
+ @Override
+ public int getApplicationEnabledSetting(String packageName, int userId) throws RemoteException {
+ return sApplicationEnabledSetting;
+ }
+
+ @Override
+ public void checkPackageStartable(String packageName, int userId) throws RemoteException {
+
+ }
+
+ @Override
+ public boolean isPackageAvailable(String packageName, int userId) throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public PackageInfo getPackageInfoVersioned(VersionedPackage versionedPackage, int flags,
+ int userId) throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public int getPackageUid(String packageName, int flags, int userId) throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public int[] getPackageGids(String packageName, int flags, int userId) throws RemoteException {
+ return new int[0];
+ }
+
+ @Override
+ public String[] currentToCanonicalPackageNames(String[] names) throws RemoteException {
+ return new String[0];
+ }
+
+ @Override
+ public String[] canonicalToCurrentPackageNames(String[] names) throws RemoteException {
+ return new String[0];
+ }
+
+ @Override
+ public PermissionInfo getPermissionInfo(String name, String packageName, int flags)
+ throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public ParceledListSlice queryPermissionsByGroup(String group, int flags)
+ throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public PermissionGroupInfo getPermissionGroupInfo(String name, int flags)
+ throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public ParceledListSlice getAllPermissionGroups(int flags) throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public ApplicationInfo getApplicationInfo(String packageName, int flags, int userId)
+ throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public ActivityInfo getActivityInfo(ComponentName className, int flags, int userId)
+ throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public boolean activitySupportsIntent(ComponentName className, Intent intent,
+ String resolvedType)
+ throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public ActivityInfo getReceiverInfo(ComponentName className, int flags, int userId)
+ throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public ServiceInfo getServiceInfo(ComponentName className, int flags, int userId)
+ throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public ProviderInfo getProviderInfo(ComponentName className, int flags, int userId)
+ throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public int checkPermission(String permName, String pkgName, int userId) throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public int checkUidPermission(String permName, int uid) throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public boolean addPermission(PermissionInfo info) throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public void removePermission(String name) throws RemoteException {
+
+ }
+
+ @Override
+ public void grantRuntimePermission(String packageName, String permissionName, int userId)
+ throws RemoteException {
+
+ }
+
+ @Override
+ public void revokeRuntimePermission(String packageName, String permissionName, int userId)
+ throws RemoteException {
+
+ }
+
+ @Override
+ public void resetRuntimePermissions() throws RemoteException {
+
+ }
+
+ @Override
+ public int getPermissionFlags(String permissionName, String packageName, int userId)
+ throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public void updatePermissionFlags(String permissionName, String packageName, int flagMask,
+ int flagValues, int userId) throws RemoteException {
+
+ }
+
+ @Override
+ public void updatePermissionFlagsForAllApps(int flagMask, int flagValues, int userId)
+ throws RemoteException {
+
+ }
+
+ @Override
+ public boolean shouldShowRequestPermissionRationale(String permissionName, String packageName,
+ int userId) throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public boolean isProtectedBroadcast(String actionName) throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public int checkSignatures(String pkg1, String pkg2) throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public int checkUidSignatures(int uid1, int uid2) throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public List<String> getAllPackages() throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public String[] getPackagesForUid(int uid) throws RemoteException {
+ return new String[0];
+ }
+
+ @Override
+ public String getNameForUid(int uid) throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public String[] getNamesForUids(int[] uids) throws RemoteException {
+ return new String[0];
+ }
+
+ @Override
+ public int getUidForSharedUser(String sharedUserName) throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public int getFlagsForUid(int uid) throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public int getPrivateFlagsForUid(int uid) throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public boolean isUidPrivileged(int uid) throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public String[] getAppOpPermissionPackages(String permissionName) throws RemoteException {
+ return new String[0];
+ }
+
+ @Override
+ public ResolveInfo resolveIntent(Intent intent, String resolvedType, int flags, int userId)
+ throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public ResolveInfo findPersistentPreferredActivity(Intent intent, int userId)
+ throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public boolean canForwardTo(Intent intent, String resolvedType, int sourceUserId,
+ int targetUserId) throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public ParceledListSlice queryIntentActivities(Intent intent, String resolvedType, int flags,
+ int userId) throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public ParceledListSlice queryIntentActivityOptions(ComponentName caller, Intent[] specifics,
+ String[] specificTypes, Intent intent, String resolvedType, int flags, int userId)
+ throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public ParceledListSlice queryIntentReceivers(Intent intent, String resolvedType, int flags,
+ int userId) throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public ResolveInfo resolveService(Intent intent, String resolvedType, int flags, int userId)
+ throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public ParceledListSlice queryIntentServices(Intent intent, String resolvedType, int flags,
+ int userId) throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public ParceledListSlice queryIntentContentProviders(Intent intent, String resolvedType,
+ int flags, int userId) throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public ParceledListSlice getInstalledPackages(int flags, int userId) throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public ParceledListSlice getPackagesHoldingPermissions(String[] permissions, int flags,
+ int userId) throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public ParceledListSlice getInstalledApplications(int flags, int userId)
+ throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public ParceledListSlice getPersistentApplications(int flags) throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public ProviderInfo resolveContentProvider(String name, int flags, int userId)
+ throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public void querySyncProviders(List<String> outNames, List<ProviderInfo> outInfo)
+ throws RemoteException {
+
+ }
+
+ @Override
+ public ParceledListSlice queryContentProviders(String processName, int uid, int flags,
+ String metaDataKey) throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public InstrumentationInfo getInstrumentationInfo(ComponentName className, int flags)
+ throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public ParceledListSlice queryInstrumentation(String targetPackage, int flags)
+ throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public void finishPackageInstall(int token, boolean didLaunch) throws RemoteException {
+
+ }
+
+ @Override
+ public void setInstallerPackageName(String targetPackage, String installerPackageName)
+ throws RemoteException {
+
+ }
+
+ @Override
+ public void setApplicationCategoryHint(String packageName, int categoryHint,
+ String callerPackageName) throws RemoteException {
+
+ }
+
+ @Override
+ public void deletePackageAsUser(String packageName, int versionCode,
+ IPackageDeleteObserver observer, int userId, int flags) throws RemoteException {
+
+ }
+
+ @Override
+ public void deletePackageVersioned(VersionedPackage versionedPackage,
+ IPackageDeleteObserver2 observer, int userId, int flags) throws RemoteException {
+
+ }
+
+ @Override
+ public String getInstallerPackageName(String packageName) throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public void resetApplicationPreferences(int userId) throws RemoteException {
+
+ }
+
+ @Override
+ public ResolveInfo getLastChosenActivity(Intent intent, String resolvedType, int flags)
+ throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public void setLastChosenActivity(Intent intent, String resolvedType, int flags,
+ IntentFilter filter, int match, ComponentName activity) throws RemoteException {
+
+ }
+
+ @Override
+ public void addPreferredActivity(IntentFilter filter, int match, ComponentName[] set,
+ ComponentName activity, int userId) throws RemoteException {
+
+ }
+
+ @Override
+ public void replacePreferredActivity(IntentFilter filter, int match, ComponentName[] set,
+ ComponentName activity, int userId) throws RemoteException {
+
+ }
+
+ @Override
+ public void clearPackagePreferredActivities(String packageName) throws RemoteException {
+
+ }
+
+ @Override
+ public int getPreferredActivities(List<IntentFilter> outFilters,
+ List<ComponentName> outActivities, String packageName) throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public void addPersistentPreferredActivity(IntentFilter filter, ComponentName activity,
+ int userId) throws RemoteException {
+
+ }
+
+ @Override
+ public void clearPackagePersistentPreferredActivities(String packageName, int userId)
+ throws RemoteException {
+
+ }
+
+ @Override
+ public void addCrossProfileIntentFilter(IntentFilter intentFilter, String ownerPackage,
+ int sourceUserId, int targetUserId, int flags) throws RemoteException {
+
+ }
+
+ @Override
+ public void clearCrossProfileIntentFilters(int sourceUserId, String ownerPackage)
+ throws RemoteException {
+
+ }
+
+ @Override
+ public String[] setDistractingPackageRestrictionsAsUser(String[] packageNames,
+ int restrictionFlags, int userId) throws RemoteException {
+ return new String[0];
+ }
+
+ @Override
+ public String[] setPackagesSuspendedAsUser(String[] packageNames, boolean suspended,
+ PersistableBundle appExtras, PersistableBundle launcherExtras, SuspendDialogInfo dialogInfo,
+ String callingPackage, int userId) throws RemoteException {
+ return new String[0];
+ }
+
+ @Override
+ public String[] getUnsuspendablePackagesForUser(String[] packageNames, int userId)
+ throws RemoteException {
+ return new String[0];
+ }
+
+ @Override
+ public boolean isPackageSuspendedForUser(String packageName, int userId)
+ throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public PersistableBundle getSuspendedPackageAppExtras(String packageName, int userId)
+ throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public byte[] getPreferredActivityBackup(int userId) throws RemoteException {
+ return new byte[0];
+ }
+
+ @Override
+ public void restorePreferredActivities(byte[] backup, int userId) throws RemoteException {
+
+ }
+
+ @Override
+ public byte[] getDefaultAppsBackup(int userId) throws RemoteException {
+ return new byte[0];
+ }
+
+ @Override
+ public void restoreDefaultApps(byte[] backup, int userId) throws RemoteException {
+
+ }
+
+ @Override
+ public byte[] getIntentFilterVerificationBackup(int userId) throws RemoteException {
+ return new byte[0];
+ }
+
+ @Override
+ public void restoreIntentFilterVerification(byte[] backup, int userId) throws RemoteException {
+
+ }
+
+ @Override
+ public byte[] getPermissionGrantBackup(int userId) throws RemoteException {
+ return new byte[0];
+ }
+
+ @Override
+ public void restorePermissionGrants(byte[] backup, int userId) throws RemoteException {
+
+ }
+
+ @Override
+ public ComponentName getHomeActivities(List<ResolveInfo> outHomeCandidates)
+ throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public void setHomeActivity(ComponentName className, int userId) throws RemoteException {
+
+ }
+
+ @Override
+ public void setComponentEnabledSetting(ComponentName componentName, int newState, int flags,
+ int userId) throws RemoteException {
+
+ }
+
+ @Override
+ public int getComponentEnabledSetting(ComponentName componentName, int userId)
+ throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public void setApplicationEnabledSetting(String packageName, int newState, int flags,
+ int userId,
+ String callingPackage) throws RemoteException {
+
+ }
+
+ @Override
+ public void logAppProcessStartIfNeeded(String processName, int uid, String seinfo,
+ String apkFile,
+ int pid) throws RemoteException {
+
+ }
+
+ @Override
+ public void flushPackageRestrictionsAsUser(int userId) throws RemoteException {
+
+ }
+
+ @Override
+ public void setPackageStoppedState(String packageName, boolean stopped, int userId)
+ throws RemoteException {
+
+ }
+
+ @Override
+ public void freeStorageAndNotify(String volumeUuid, long freeStorageSize, int storageFlags,
+ IPackageDataObserver observer) throws RemoteException {
+
+ }
+
+ @Override
+ public void freeStorage(String volumeUuid, long freeStorageSize, int storageFlags,
+ IntentSender pi) throws RemoteException {
+
+ }
+
+ @Override
+ public void deleteApplicationCacheFiles(String packageName, IPackageDataObserver observer)
+ throws RemoteException {
+
+ }
+
+ @Override
+ public void deleteApplicationCacheFilesAsUser(String packageName, int userId,
+ IPackageDataObserver observer) throws RemoteException {
+
+ }
+
+ @Override
+ public void clearApplicationUserData(String packageName, IPackageDataObserver observer,
+ int userId) throws RemoteException {
+
+ }
+
+ @Override
+ public void clearApplicationProfileData(String packageName) throws RemoteException {
+
+ }
+
+ @Override
+ public void getPackageSizeInfo(String packageName, int userHandle,
+ IPackageStatsObserver observer)
+ throws RemoteException {
+
+ }
+
+ @Override
+ public String[] getSystemSharedLibraryNames() throws RemoteException {
+ return new String[0];
+ }
+
+ @Override
+ public ParceledListSlice getSystemAvailableFeatures() throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public boolean hasSystemFeature(String name, int version) throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public void enterSafeMode() throws RemoteException {
+
+ }
+
+ @Override
+ public boolean isSafeMode() throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public void systemReady() throws RemoteException {
+
+ }
+
+ @Override
+ public boolean hasSystemUidErrors() throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public void performFstrimIfNeeded() throws RemoteException {
+
+ }
+
+ @Override
+ public void updatePackagesIfNeeded() throws RemoteException {
+
+ }
+
+ @Override
+ public void notifyPackageUse(String packageName, int reason) throws RemoteException {
+
+ }
+
+ @Override
+ public void notifyDexLoad(String loadingPackageName, List<String> classLoadersNames,
+ List<String> classPaths, String loaderIsa) throws RemoteException {
+
+ }
+
+ @Override
+ public void registerDexModule(String packageName, String dexModulePath, boolean isSharedModule,
+ IDexModuleRegisterCallback callback) throws RemoteException {
+
+ }
+
+ @Override
+ public boolean performDexOptMode(String packageName, boolean checkProfiles,
+ String targetCompilerFilter, boolean force, boolean bootComplete, String splitName)
+ throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public boolean performDexOptSecondary(String packageName, String targetCompilerFilter,
+ boolean force) throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public boolean compileLayouts(String packageName) throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public void dumpProfiles(String packageName) throws RemoteException {
+
+ }
+
+ @Override
+ public void forceDexOpt(String packageName) throws RemoteException {
+
+ }
+
+ @Override
+ public boolean runBackgroundDexoptJob(List<String> packageNames) throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public void reconcileSecondaryDexFiles(String packageName) throws RemoteException {
+
+ }
+
+ @Override
+ public int getMoveStatus(int moveId) throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public void registerMoveCallback(IPackageMoveObserver callback) throws RemoteException {
+
+ }
+
+ @Override
+ public void unregisterMoveCallback(IPackageMoveObserver callback) throws RemoteException {
+
+ }
+
+ @Override
+ public int movePackage(String packageName, String volumeUuid) throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public int movePrimaryStorage(String volumeUuid) throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public boolean addPermissionAsync(PermissionInfo info) throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public boolean setInstallLocation(int loc) throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public int getInstallLocation() throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public int installExistingPackageAsUser(String packageName, int userId, int installFlags,
+ int installReason) throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public void verifyPendingInstall(int id, int verificationCode) throws RemoteException {
+
+ }
+
+ @Override
+ public void extendVerificationTimeout(int id, int verificationCodeAtTimeout,
+ long millisecondsToDelay) throws RemoteException {
+
+ }
+
+ @Override
+ public void verifyIntentFilter(int id, int verificationCode, List<String> failedDomains)
+ throws RemoteException {
+
+ }
+
+ @Override
+ public int getIntentVerificationStatus(String packageName, int userId) throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public boolean updateIntentVerificationStatus(String packageName, int status, int userId)
+ throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public ParceledListSlice getIntentFilterVerifications(String packageName)
+ throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public ParceledListSlice getAllIntentFilters(String packageName) throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public boolean setDefaultBrowserPackageName(String packageName, int userId)
+ throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public String getDefaultBrowserPackageName(int userId) throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public VerifierDeviceIdentity getVerifierDeviceIdentity() throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public boolean isFirstBoot() throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public boolean isOnlyCoreApps() throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public boolean isUpgrade() throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public void setPermissionEnforced(String permission, boolean enforced) throws RemoteException {
+
+ }
+
+ @Override
+ public boolean isPermissionEnforced(String permission) throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public boolean isStorageLow() throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public boolean setApplicationHiddenSettingAsUser(String packageName, boolean hidden, int userId)
+ throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public boolean getApplicationHiddenSettingAsUser(String packageName, int userId)
+ throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public void setSystemAppHiddenUntilInstalled(String packageName, boolean hidden)
+ throws RemoteException {
+
+ }
+
+ @Override
+ public boolean setSystemAppInstallState(String packageName, boolean installed, int userId)
+ throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public IPackageInstaller getPackageInstaller() throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public boolean setBlockUninstallForUser(String packageName, boolean blockUninstall, int userId)
+ throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public boolean getBlockUninstallForUser(String packageName, int userId) throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public KeySet getKeySetByAlias(String packageName, String alias) throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public KeySet getSigningKeySet(String packageName) throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public boolean isPackageSignedByKeySet(String packageName, KeySet ks) throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public boolean isPackageSignedByKeySetExactly(String packageName, KeySet ks)
+ throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public void addOnPermissionsChangeListener(IOnPermissionsChangeListener listener)
+ throws RemoteException {
+
+ }
+
+ @Override
+ public void removeOnPermissionsChangeListener(IOnPermissionsChangeListener listener)
+ throws RemoteException {
+
+ }
+
+ @Override
+ public void grantDefaultPermissionsToEnabledCarrierApps(String[] packageNames, int userId)
+ throws RemoteException {
+
+ }
+
+ @Override
+ public void grantDefaultPermissionsToEnabledImsServices(String[] packageNames, int userId)
+ throws RemoteException {
+
+ }
+
+ @Override
+ public void grantDefaultPermissionsToEnabledTelephonyDataServices(String[] packageNames,
+ int userId) throws RemoteException {
+
+ }
+
+ @Override
+ public void revokeDefaultPermissionsFromDisabledTelephonyDataServices(String[] packageNames,
+ int userId) throws RemoteException {
+
+ }
+
+ @Override
+ public void grantDefaultPermissionsToActiveLuiApp(String packageName, int userId)
+ throws RemoteException {
+
+ }
+
+ @Override
+ public void revokeDefaultPermissionsFromLuiApps(String[] packageNames, int userId)
+ throws RemoteException {
+
+ }
+
+ @Override
+ public boolean isPermissionRevokedByPolicy(String permission, String packageName, int userId)
+ throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public String getPermissionControllerPackageName() throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public ParceledListSlice getInstantApps(int userId) throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public byte[] getInstantAppCookie(String packageName, int userId) throws RemoteException {
+ return new byte[0];
+ }
+
+ @Override
+ public boolean setInstantAppCookie(String packageName, byte[] cookie, int userId)
+ throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public Bitmap getInstantAppIcon(String packageName, int userId) throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public boolean isInstantApp(String packageName, int userId) throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public boolean setRequiredForSystemUser(String packageName, boolean systemUserApp)
+ throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public void setUpdateAvailable(String packageName, boolean updateAvaialble)
+ throws RemoteException {
+
+ }
+
+ @Override
+ public String getServicesSystemSharedLibraryPackageName() throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public String getSharedSystemSharedLibraryPackageName() throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public ChangedPackages getChangedPackages(int sequenceNumber, int userId)
+ throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public boolean isPackageDeviceAdminOnAnyUser(String packageName) throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public int getInstallReason(String packageName, int userId) throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public ParceledListSlice getSharedLibraries(String packageName, int flags, int userId)
+ throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public boolean canRequestPackageInstalls(String packageName, int userId)
+ throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public void deletePreloadsFileCache() throws RemoteException {
+
+ }
+
+ @Override
+ public ComponentName getInstantAppResolverComponent() throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public ComponentName getInstantAppResolverSettingsComponent() throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public ComponentName getInstantAppInstallerComponent() throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public String getInstantAppAndroidId(String packageName, int userId) throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public IArtManager getArtManager() throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public void setHarmfulAppWarning(String packageName, CharSequence warning, int userId)
+ throws RemoteException {
+
+ }
+
+ @Override
+ public CharSequence getHarmfulAppWarning(String packageName, int userId)
+ throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public boolean hasSigningCertificate(String packageName, byte[] signingCertificate, int flags)
+ throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public boolean hasUidSigningCertificate(int uid, byte[] signingCertificate, int flags)
+ throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public String getSystemTextClassifierPackageName() throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public String getWellbeingPackageName() throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public boolean isPackageStateProtected(String packageName, int userId) throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public void sendDeviceCustomizationReadyBroadcast() throws RemoteException {
+
+ }
+
+ @Override
+ public List<ModuleInfo> getInstalledModules(int flags) throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public ModuleInfo getModuleInfo(String packageName, int flags) throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public IBinder asBinder() {
+ return null;
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java b/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java
index 525135c..7172752 100644
--- a/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java
+++ b/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java
@@ -48,11 +48,7 @@
@Override
public PackageInfo getPackageInfo(String packageName, int flags)
throws NameNotFoundException {
- if (sPackageInfo == null) {
- throw new NameNotFoundException();
- }
-
- return sPackageInfo;
+ return getPackageInfoAsUser(packageName, flags, UserHandle.USER_SYSTEM);
}
@Override
@@ -64,7 +60,11 @@
@Override
public PackageInfo getPackageInfoAsUser(String packageName, int flags, int userId)
throws NameNotFoundException {
- return null;
+ if (sPackageInfo == null) {
+ throw new NameNotFoundException();
+ }
+
+ return sPackageInfo;
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java
index 479a19b..a92b576 100644
--- a/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java
@@ -29,13 +29,14 @@
import android.content.pm.Signature;
import android.content.pm.SigningInfo;
import android.os.Process;
+import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.backup.UserBackupManagerService;
-import com.android.server.backup.testutils.PackageManagerStub;
+import com.android.server.backup.testutils.IPackageManagerStub;
import org.junit.Before;
import org.junit.Test;
@@ -53,13 +54,17 @@
private static final Signature SIGNATURE_3 = generateSignature((byte) 3);
private static final Signature SIGNATURE_4 = generateSignature((byte) 4);
- private PackageManagerStub mPackageManagerStub;
+ private IPackageManagerStub mPackageManagerStub;
private PackageManagerInternal mMockPackageManagerInternal;
+ private int mUserId;
+
@Before
public void setUp() throws Exception {
- mPackageManagerStub = new PackageManagerStub();
+ mPackageManagerStub = new IPackageManagerStub();
mMockPackageManagerInternal = mock(PackageManagerInternal.class);
+
+ mUserId = UserHandle.USER_SYSTEM;
}
@Test
@@ -71,7 +76,7 @@
applicationInfo.packageName = TEST_PACKAGE_NAME;
boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo,
- mPackageManagerStub);
+ mPackageManagerStub, mUserId);
assertThat(isEligible).isFalse();
}
@@ -86,7 +91,7 @@
applicationInfo.packageName = TEST_PACKAGE_NAME;
boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo,
- mPackageManagerStub);
+ mPackageManagerStub, mUserId);
assertThat(isEligible).isFalse();
}
@@ -100,7 +105,7 @@
applicationInfo.packageName = UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo,
- mPackageManagerStub);
+ mPackageManagerStub, mUserId);
assertThat(isEligible).isFalse();
}
@@ -114,11 +119,11 @@
applicationInfo.backupAgentName = CUSTOM_BACKUP_AGENT_NAME;
applicationInfo.packageName = TEST_PACKAGE_NAME;
- PackageManagerStub.sApplicationEnabledSetting =
+ IPackageManagerStub.sApplicationEnabledSetting =
PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo,
- mPackageManagerStub);
+ mPackageManagerStub, mUserId);
assertThat(isEligible).isTrue();
}
@@ -132,11 +137,11 @@
applicationInfo.backupAgentName = null;
applicationInfo.packageName = TEST_PACKAGE_NAME;
- PackageManagerStub.sApplicationEnabledSetting =
+ IPackageManagerStub.sApplicationEnabledSetting =
PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo,
- mPackageManagerStub);
+ mPackageManagerStub, mUserId);
assertThat(isEligible).isTrue();
}
@@ -150,11 +155,11 @@
applicationInfo.backupAgentName = CUSTOM_BACKUP_AGENT_NAME;
applicationInfo.packageName = TEST_PACKAGE_NAME;
- PackageManagerStub.sApplicationEnabledSetting =
+ IPackageManagerStub.sApplicationEnabledSetting =
PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo,
- mPackageManagerStub);
+ mPackageManagerStub, mUserId);
assertThat(isEligible).isTrue();
}
@@ -168,11 +173,11 @@
applicationInfo.backupAgentName = CUSTOM_BACKUP_AGENT_NAME;
applicationInfo.packageName = TEST_PACKAGE_NAME;
- PackageManagerStub.sApplicationEnabledSetting =
+ IPackageManagerStub.sApplicationEnabledSetting =
PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo,
- mPackageManagerStub);
+ mPackageManagerStub, mUserId);
assertThat(isEligible).isFalse();
}
@@ -186,11 +191,11 @@
applicationInfo.backupAgentName = null;
applicationInfo.packageName = TEST_PACKAGE_NAME;
- PackageManagerStub.sApplicationEnabledSetting =
+ IPackageManagerStub.sApplicationEnabledSetting =
PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo,
- mPackageManagerStub);
+ mPackageManagerStub, mUserId);
assertThat(isEligible).isFalse();
}
@@ -204,11 +209,11 @@
applicationInfo.backupAgentName = CUSTOM_BACKUP_AGENT_NAME;
applicationInfo.packageName = TEST_PACKAGE_NAME;
- PackageManagerStub.sApplicationEnabledSetting =
+ IPackageManagerStub.sApplicationEnabledSetting =
PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo,
- mPackageManagerStub);
+ mPackageManagerStub, mUserId);
assertThat(isEligible).isFalse();
}
@@ -222,10 +227,11 @@
applicationInfo.packageName = TEST_PACKAGE_NAME;
applicationInfo.enabled = true;
- PackageManagerStub.sApplicationEnabledSetting =
+ IPackageManagerStub.sApplicationEnabledSetting =
PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
- boolean isDisabled = AppBackupUtils.appIsDisabled(applicationInfo, mPackageManagerStub);
+ boolean isDisabled = AppBackupUtils.appIsDisabled(applicationInfo, mPackageManagerStub,
+ mUserId);
assertThat(isDisabled).isFalse();
}
@@ -239,10 +245,11 @@
applicationInfo.packageName = TEST_PACKAGE_NAME;
applicationInfo.enabled = false;
- PackageManagerStub.sApplicationEnabledSetting =
+ IPackageManagerStub.sApplicationEnabledSetting =
PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
- boolean isDisabled = AppBackupUtils.appIsDisabled(applicationInfo, mPackageManagerStub);
+ boolean isDisabled = AppBackupUtils.appIsDisabled(applicationInfo, mPackageManagerStub,
+ mUserId);
assertThat(isDisabled).isTrue();
}
@@ -255,10 +262,11 @@
applicationInfo.backupAgentName = CUSTOM_BACKUP_AGENT_NAME;
applicationInfo.packageName = TEST_PACKAGE_NAME;
- PackageManagerStub.sApplicationEnabledSetting =
+ IPackageManagerStub.sApplicationEnabledSetting =
PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
- boolean isDisabled = AppBackupUtils.appIsDisabled(applicationInfo, mPackageManagerStub);
+ boolean isDisabled = AppBackupUtils.appIsDisabled(applicationInfo, mPackageManagerStub,
+ mUserId);
assertThat(isDisabled).isFalse();
}
@@ -271,10 +279,11 @@
applicationInfo.backupAgentName = CUSTOM_BACKUP_AGENT_NAME;
applicationInfo.packageName = TEST_PACKAGE_NAME;
- PackageManagerStub.sApplicationEnabledSetting =
+ IPackageManagerStub.sApplicationEnabledSetting =
PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
- boolean isDisabled = AppBackupUtils.appIsDisabled(applicationInfo, mPackageManagerStub);
+ boolean isDisabled = AppBackupUtils.appIsDisabled(applicationInfo, mPackageManagerStub,
+ mUserId);
assertThat(isDisabled).isTrue();
}
@@ -287,10 +296,11 @@
applicationInfo.backupAgentName = CUSTOM_BACKUP_AGENT_NAME;
applicationInfo.packageName = TEST_PACKAGE_NAME;
- PackageManagerStub.sApplicationEnabledSetting =
+ IPackageManagerStub.sApplicationEnabledSetting =
PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
- boolean isDisabled = AppBackupUtils.appIsDisabled(applicationInfo, mPackageManagerStub);
+ boolean isDisabled = AppBackupUtils.appIsDisabled(applicationInfo, mPackageManagerStub,
+ mUserId);
assertThat(isDisabled).isTrue();
}
@@ -303,10 +313,11 @@
applicationInfo.backupAgentName = CUSTOM_BACKUP_AGENT_NAME;
applicationInfo.packageName = TEST_PACKAGE_NAME;
- PackageManagerStub.sApplicationEnabledSetting =
+ IPackageManagerStub.sApplicationEnabledSetting =
PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
- boolean isDisabled = AppBackupUtils.appIsDisabled(applicationInfo, mPackageManagerStub);
+ boolean isDisabled = AppBackupUtils.appIsDisabled(applicationInfo, mPackageManagerStub,
+ mUserId);
assertThat(isDisabled).isTrue();
}
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
index d43b677..5fcce67 100644
--- a/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
@@ -44,6 +44,7 @@
import android.content.pm.SigningInfo;
import android.os.Bundle;
import android.os.Process;
+import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import androidx.test.InstrumentationRegistry;
@@ -88,12 +89,14 @@
private final PackageManagerStub mPackageManagerStub = new PackageManagerStub();
private Context mContext;
+ private int mUserId;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mContext = InstrumentationRegistry.getContext();
+ mUserId = UserHandle.USER_SYSTEM;
}
@Test
@@ -146,7 +149,7 @@
fileMetadata);
RestorePolicy restorePolicy = tarBackupReader.chooseRestorePolicy(
mPackageManagerStub, false /* allowApks */, fileMetadata, signatures,
- mMockPackageManagerInternal);
+ mMockPackageManagerInternal, mUserId);
assertThat(restorePolicy).isEqualTo(RestorePolicy.IGNORE);
assertThat(fileMetadata.packageName).isEqualTo(TEST_PACKAGE_NAME);
@@ -160,7 +163,7 @@
fileMetadata);
restorePolicy = tarBackupReader.chooseRestorePolicy(
mPackageManagerStub, false /* allowApks */, fileMetadata, signatures,
- mMockPackageManagerInternal);
+ mMockPackageManagerInternal, mUserId);
assertThat(restorePolicy).isEqualTo(RestorePolicy.IGNORE);
assertThat(fileMetadata.packageName).isEqualTo(TEST_PACKAGE_NAME);
@@ -223,7 +226,7 @@
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
true /* allowApks */, new FileMetadata(), null /* signatures */,
- mMockPackageManagerInternal);
+ mMockPackageManagerInternal, mUserId);
assertThat(policy).isEqualTo(RestorePolicy.IGNORE);
verifyZeroInteractions(mBackupManagerMonitorMock);
@@ -244,7 +247,7 @@
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
true /* allowApks */, info, new Signature[0] /* signatures */,
- mMockPackageManagerInternal);
+ mMockPackageManagerInternal, mUserId);
assertThat(policy).isEqualTo(RestorePolicy.ACCEPT_IF_APK);
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
@@ -269,7 +272,7 @@
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
true /* allowApks */, info, new Signature[0] /* signatures */,
- mMockPackageManagerInternal);
+ mMockPackageManagerInternal, mUserId);
assertThat(policy).isEqualTo(RestorePolicy.ACCEPT_IF_APK);
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
@@ -295,7 +298,7 @@
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
false /* allowApks */, new FileMetadata(), new Signature[0] /* signatures */,
- mMockPackageManagerInternal);
+ mMockPackageManagerInternal, mUserId);
assertThat(policy).isEqualTo(RestorePolicy.IGNORE);
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
@@ -320,7 +323,7 @@
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
false /* allowApks */, new FileMetadata(), new Signature[0] /* signatures */,
- mMockPackageManagerInternal);
+ mMockPackageManagerInternal, mUserId);
assertThat(policy).isEqualTo(RestorePolicy.IGNORE);
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
@@ -347,7 +350,7 @@
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
false /* allowApks */, new FileMetadata(), new Signature[0] /* signatures */,
- mMockPackageManagerInternal);
+ mMockPackageManagerInternal, mUserId);
assertThat(policy).isEqualTo(RestorePolicy.IGNORE);
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
@@ -381,7 +384,8 @@
PackageManagerStub.sPackageInfo = packageInfo;
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
- false /* allowApks */, new FileMetadata(), signatures, mMockPackageManagerInternal);
+ false /* allowApks */, new FileMetadata(), signatures,
+ mMockPackageManagerInternal, mUserId);
assertThat(policy).isEqualTo(RestorePolicy.IGNORE);
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
@@ -419,7 +423,8 @@
doReturn(true).when(mMockPackageManagerInternal).isDataRestoreSafe(FAKE_SIGNATURE_1,
packageInfo.packageName);
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
- false /* allowApks */, new FileMetadata(), signatures, mMockPackageManagerInternal);
+ false /* allowApks */, new FileMetadata(), signatures,
+ mMockPackageManagerInternal, mUserId);
assertThat(policy).isEqualTo(RestorePolicy.ACCEPT);
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
@@ -456,7 +461,8 @@
doReturn(true).when(mMockPackageManagerInternal).isDataRestoreSafe(FAKE_SIGNATURE_1,
packageInfo.packageName);
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
- false /* allowApks */, new FileMetadata(), signatures, mMockPackageManagerInternal);
+ false /* allowApks */, new FileMetadata(), signatures,
+ mMockPackageManagerInternal, mUserId);
assertThat(policy).isEqualTo(RestorePolicy.ACCEPT);
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
@@ -497,7 +503,8 @@
doReturn(true).when(mMockPackageManagerInternal).isDataRestoreSafe(FAKE_SIGNATURE_1,
packageInfo.packageName);
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
- false /* allowApks */, info, signatures, mMockPackageManagerInternal);
+ false /* allowApks */, info, signatures, mMockPackageManagerInternal,
+ mUserId);
assertThat(policy).isEqualTo(RestorePolicy.ACCEPT);
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
@@ -540,7 +547,8 @@
doReturn(true).when(mMockPackageManagerInternal).isDataRestoreSafe(FAKE_SIGNATURE_1,
packageInfo.packageName);
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
- true /* allowApks */, info, signatures, mMockPackageManagerInternal);
+ true /* allowApks */, info, signatures, mMockPackageManagerInternal,
+ mUserId);
assertThat(policy).isEqualTo(RestorePolicy.ACCEPT_IF_APK);
verifyNoMoreInteractions(mBackupManagerMonitorMock);
@@ -579,7 +587,8 @@
doReturn(true).when(mMockPackageManagerInternal).isDataRestoreSafe(FAKE_SIGNATURE_1,
packageInfo.packageName);
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
- false /* allowApks */, info, signatures, mMockPackageManagerInternal);
+ false /* allowApks */, info, signatures, mMockPackageManagerInternal,
+ mUserId);
assertThat(policy).isEqualTo(RestorePolicy.IGNORE);
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
diff --git a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
index 860656b..8d9b3cf 100644
--- a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
+++ b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
@@ -45,6 +45,8 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
public class UsageStatsDatabaseTest {
+
+ private static final int MAX_TESTED_VERSION = 4;
protected Context mContext;
private UsageStatsDatabase mUsageStatsDatabase;
private File mTestDir;
@@ -131,8 +133,8 @@
for (int i = 0; i < numberOfEvents; i++) {
Event event = new Event();
- final int packageInt = ((i / 3) % 7);
- event.mPackage = "fake.package.name" + packageInt; //clusters of 3 events from 7 "apps"
+ final int packageInt = ((i / 3) % 7); //clusters of 3 events from 7 "apps"
+ event.mPackage = "fake.package.name" + packageInt;
if (packageInt == 3) {
// Third app is an instant app
event.mFlags |= Event.FLAG_IS_PACKAGE_INSTANT_APP;
@@ -144,6 +146,13 @@
event.mEventType = i % (MAX_EVENT_TYPE + 1); //"random" event type
event.mInstanceId = instanceId;
+
+ final int rootPackageInt = (i % 5); // 5 "apps" start each task
+ event.mTaskRootPackage = "fake.package.name" + rootPackageInt;
+
+ final int rootClassInt = i % 6;
+ event.mTaskRootClass = ".fake.class.name" + rootClassInt;
+
switch (event.mEventType) {
case Event.CONFIGURATION_CHANGE:
//empty config,
@@ -163,7 +172,7 @@
break;
}
- mIntervalStats.events.insert(event);
+ mIntervalStats.addEvent(event);
mIntervalStats.update(event.mPackage, event.mClass, event.mTimeStamp, event.mEventType,
event.mInstanceId);
@@ -234,31 +243,40 @@
assertEquals(us1.mChooserCounts, us2.mChooserCounts);
}
- void compareUsageEvent(Event e1, Event e2, int debugId) {
- assertEquals(e1.mPackage, e2.mPackage, "Usage event " + debugId);
- assertEquals(e1.mClass, e2.mClass, "Usage event " + debugId);
- assertEquals(e1.mTimeStamp, e2.mTimeStamp, "Usage event " + debugId);
- assertEquals(e1.mEventType, e2.mEventType, "Usage event " + debugId);
- switch (e1.mEventType) {
- case Event.CONFIGURATION_CHANGE:
- assertEquals(e1.mConfiguration, e2.mConfiguration,
- "Usage event " + debugId + e2.mConfiguration.toString());
- break;
- case Event.SHORTCUT_INVOCATION:
- assertEquals(e1.mShortcutId, e2.mShortcutId, "Usage event " + debugId);
- break;
- case Event.STANDBY_BUCKET_CHANGED:
- assertEquals(e1.mBucketAndReason, e2.mBucketAndReason, "Usage event " + debugId);
- break;
- case Event.NOTIFICATION_INTERRUPTION:
- assertEquals(e1.mNotificationChannelId, e2.mNotificationChannelId,
- "Usage event " + debugId);
- break;
+ void compareUsageEvent(Event e1, Event e2, int debugId, int minVersion) {
+ switch (minVersion) {
+ case 4: // test fields added in version 4
+ assertEquals(e1.mInstanceId, e2.mInstanceId, "Usage event " + debugId);
+ assertEquals(e1.mTaskRootPackage, e2.mTaskRootPackage, "Usage event " + debugId);
+ assertEquals(e1.mTaskRootClass, e2.mTaskRootClass, "Usage event " + debugId);
+ // fallthrough
+ default:
+ assertEquals(e1.mPackage, e2.mPackage, "Usage event " + debugId);
+ assertEquals(e1.mClass, e2.mClass, "Usage event " + debugId);
+ assertEquals(e1.mTimeStamp, e2.mTimeStamp, "Usage event " + debugId);
+ assertEquals(e1.mEventType, e2.mEventType, "Usage event " + debugId);
+ switch (e1.mEventType) {
+ case Event.CONFIGURATION_CHANGE:
+ assertEquals(e1.mConfiguration, e2.mConfiguration,
+ "Usage event " + debugId + e2.mConfiguration.toString());
+ break;
+ case Event.SHORTCUT_INVOCATION:
+ assertEquals(e1.mShortcutId, e2.mShortcutId, "Usage event " + debugId);
+ break;
+ case Event.STANDBY_BUCKET_CHANGED:
+ assertEquals(e1.mBucketAndReason, e2.mBucketAndReason,
+ "Usage event " + debugId);
+ break;
+ case Event.NOTIFICATION_INTERRUPTION:
+ assertEquals(e1.mNotificationChannelId, e2.mNotificationChannelId,
+ "Usage event " + debugId);
+ break;
+ }
+ assertEquals(e1.mFlags, e2.mFlags);
}
- assertEquals(e1.mFlags, e2.mFlags);
}
- void compareIntervalStats(IntervalStats stats1, IntervalStats stats2) {
+ void compareIntervalStats(IntervalStats stats1, IntervalStats stats2, int minVersion) {
assertEquals(stats1.majorVersion, stats2.majorVersion);
assertEquals(stats1.minorVersion, stats2.minorVersion);
assertEquals(stats1.beginTime, stats2.beginTime);
@@ -311,7 +329,7 @@
} else {
assertEquals(stats1.events.size(), stats2.events.size());
for (int i = 0; i < stats1.events.size(); i++) {
- compareUsageEvent(stats1.events.get(i), stats2.events.get(i), i);
+ compareUsageEvent(stats1.events.get(i), stats2.events.get(i), i, minVersion);
}
}
}
@@ -326,7 +344,7 @@
mIntervalStatsVerifier);
assertEquals(1, stats.size());
- compareIntervalStats(mIntervalStats, stats.get(0));
+ compareIntervalStats(mIntervalStats, stats.get(0), MAX_TESTED_VERSION);
}
/**
@@ -359,8 +377,10 @@
mIntervalStatsVerifier);
assertEquals(1, stats.size());
+
+ final int minVersion = oldVersion < newVersion ? oldVersion : newVersion;
// The written and read IntervalStats should match
- compareIntervalStats(mIntervalStats, stats.get(0));
+ compareIntervalStats(mIntervalStats, stats.get(0), minVersion);
}
/**
@@ -401,7 +421,7 @@
if (mIntervalStats.events != null) mIntervalStats.events.clear();
// The written and read IntervalStats should match
- compareIntervalStats(mIntervalStats, stats.get(0));
+ compareIntervalStats(mIntervalStats, stats.get(0), version);
}
/**
diff --git a/services/usage/java/com/android/server/usage/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java
index 9a5bd13..f1ddfe4 100644
--- a/services/usage/java/com/android/server/usage/IntervalStats.java
+++ b/services/usage/java/com/android/server/usage/IntervalStats.java
@@ -218,6 +218,14 @@
case (int) IntervalStatsProto.Event.INSTANCE_ID:
event.mInstanceId = parser.readInt(IntervalStatsProto.Event.INSTANCE_ID);
break;
+ case (int) IntervalStatsProto.Event.TASK_ROOT_PACKAGE_INDEX:
+ event.mTaskRootPackage = getCachedStringRef(stringPool.get(
+ parser.readInt(IntervalStatsProto.Event.TASK_ROOT_PACKAGE_INDEX) - 1));
+ break;
+ case (int) IntervalStatsProto.Event.TASK_ROOT_CLASS_INDEX:
+ event.mTaskRootClass = getCachedStringRef(stringPool.get(
+ parser.readInt(IntervalStatsProto.Event.TASK_ROOT_CLASS_INDEX) - 1));
+ break;
case ProtoInputStream.NO_MORE_FIELDS:
// Handle default values for certain events types
switch (event.mEventType) {
@@ -332,6 +340,12 @@
if (event.mClass != null) {
event.mClass = getCachedStringRef(event.mClass);
}
+ if (event.mTaskRootPackage != null) {
+ event.mTaskRootPackage = getCachedStringRef(event.mTaskRootPackage);
+ }
+ if (event.mTaskRootClass != null) {
+ event.mTaskRootClass = getCachedStringRef(event.mTaskRootClass);
+ }
if (event.mEventType == NOTIFICATION_INTERRUPTION) {
event.mNotificationChannelId = getCachedStringRef(event.mNotificationChannelId);
}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsProto.java b/services/usage/java/com/android/server/usage/UsageStatsProto.java
index d706537..11d49eb 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsProto.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsProto.java
@@ -442,6 +442,28 @@
proto.write(IntervalStatsProto.Event.FLAGS, event.mFlags);
proto.write(IntervalStatsProto.Event.TYPE, event.mEventType);
proto.write(IntervalStatsProto.Event.INSTANCE_ID, event.mInstanceId);
+ if (event.mTaskRootPackage != null) {
+ final int taskRootPackageIndex = stats.mStringCache.indexOf(event.mTaskRootPackage);
+ if (taskRootPackageIndex >= 0) {
+ proto.write(IntervalStatsProto.Event.TASK_ROOT_PACKAGE_INDEX,
+ taskRootPackageIndex + 1);
+ } else {
+ // Task root package not in Stringpool for some reason.
+ Slog.w(TAG, "Usage event task root package name (" + event.mTaskRootPackage
+ + ") not found in IntervalStats string cache");
+ }
+ }
+ if (event.mTaskRootClass != null) {
+ final int taskRootClassIndex = stats.mStringCache.indexOf(event.mTaskRootClass);
+ if (taskRootClassIndex >= 0) {
+ proto.write(IntervalStatsProto.Event.TASK_ROOT_CLASS_INDEX,
+ taskRootClassIndex + 1);
+ } else {
+ // Task root class not in Stringpool for some reason.
+ Slog.w(TAG, "Usage event task root class name (" + event.mTaskRootClass
+ + ") not found in IntervalStats string cache");
+ }
+ }
switch (event.mEventType) {
case UsageEvents.Event.CONFIGURATION_CHANGE:
if (event.mConfiguration != null) {
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 76a3aa8..6ad698b 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -22,6 +22,8 @@
import static android.app.usage.UsageEvents.Event.FLUSH_TO_DISK;
import static android.app.usage.UsageEvents.Event.NOTIFICATION_INTERRUPTION;
import static android.app.usage.UsageEvents.Event.SHORTCUT_INVOCATION;
+import static android.app.usage.UsageStatsManager.USAGE_SOURCE_CURRENT_ACTIVITY;
+import static android.app.usage.UsageStatsManager.USAGE_SOURCE_TASK_ROOT_ACTIVITY;
import android.Manifest;
import android.app.ActivityManager;
@@ -39,6 +41,7 @@
import android.app.usage.UsageStats;
import android.app.usage.UsageStatsManager;
import android.app.usage.UsageStatsManager.StandbyBuckets;
+import android.app.usage.UsageStatsManager.UsageSource;
import android.app.usage.UsageStatsManagerInternal;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -65,6 +68,7 @@
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
+import android.provider.Settings;
import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
@@ -132,6 +136,7 @@
private File mUsageStatsDir;
long mRealTimeSnapshot;
long mSystemTimeSnapshot;
+ int mUsageSource;
/** Manages the standby state of apps. */
AppStandbyController mAppStandby;
@@ -258,6 +263,7 @@
} else {
Slog.w(TAG, "Missing procfs interface: " + KERNEL_COUNTER_FILE);
}
+ readUsageSourceSetting();
}
}
@@ -268,6 +274,13 @@
return mDpmInternal;
}
+ private void readUsageSourceSetting() {
+ synchronized (mLock) {
+ mUsageSource = Settings.Global.getInt(getContext().getContentResolver(),
+ Settings.Global.APP_TIME_LIMIT_USAGE_SOURCE, USAGE_SOURCE_TASK_ROOT_ACTIVITY);
+ }
+ }
+
private class UserActionsReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
@@ -459,12 +472,28 @@
service.reportEvent(event);
mAppStandby.reportEvent(event, elapsedRealtime, userId);
+
+ String packageName;
+
+ switch(mUsageSource) {
+ case USAGE_SOURCE_CURRENT_ACTIVITY:
+ packageName = event.getPackageName();
+ break;
+ case USAGE_SOURCE_TASK_ROOT_ACTIVITY:
+ default:
+ packageName = event.getTaskRootPackageName();
+ if (packageName == null) {
+ packageName = event.getPackageName();
+ }
+ break;
+ }
+
switch (event.mEventType) {
case Event.ACTIVITY_RESUMED:
synchronized (mVisibleActivities) {
mVisibleActivities.put(event.mInstanceId, event.getClassName());
try {
- mAppTimeLimit.noteUsageStart(event.getPackageName(), userId);
+ mAppTimeLimit.noteUsageStart(packageName, userId);
} catch (IllegalArgumentException iae) {
Slog.e(TAG, "Failed to note usage start", iae);
}
@@ -496,7 +525,7 @@
synchronized (mVisibleActivities) {
if (mVisibleActivities.removeReturnOld(event.mInstanceId) != null) {
try {
- mAppTimeLimit.noteUsageStop(event.getPackageName(), userId);
+ mAppTimeLimit.noteUsageStop(packageName, userId);
} catch (IllegalArgumentException iae) {
Slog.w(TAG, "Failed to note usage stop", iae);
}
@@ -638,7 +667,7 @@
* Called by the Binder stub.
*/
UsageEvents queryEventsForPackage(int userId, long beginTime, long endTime,
- String packageName) {
+ String packageName, boolean includeTaskRoot) {
synchronized (mLock) {
final long timeNow = checkAndGetTimeLocked();
if (!validRange(timeNow, beginTime, endTime)) {
@@ -647,7 +676,7 @@
final UserUsageStatsService service =
getUserDataAndInitializeIfNeededLocked(userId, timeNow);
- return service.queryEventsForPackage(beginTime, endTime, packageName);
+ return service.queryEventsForPackage(beginTime, endTime, packageName, includeTaskRoot);
}
}
@@ -738,6 +767,10 @@
mAppStandby.dumpState(args, pw);
}
+ idpw.println();
+ idpw.printPair("Usage Source", UsageStatsManager.usageSourceToString(mUsageSource));
+ idpw.println();
+
mAppTimeLimit.dump(null, pw);
}
}
@@ -808,7 +841,7 @@
return mode == AppOpsManager.MODE_ALLOWED;
}
- private boolean hasObserverPermission(String callingPackage) {
+ private boolean hasObserverPermission() {
final int callingUid = Binder.getCallingUid();
DevicePolicyManagerInternal dpmInternal = getDpmInternal();
if (callingUid == Process.SYSTEM_UID
@@ -939,10 +972,12 @@
final int callingUserId = UserHandle.getUserId(callingUid);
checkCallerIsSameApp(callingPackage);
+ final boolean includeTaskRoot = hasPermission(callingPackage);
+
final long token = Binder.clearCallingIdentity();
try {
return UsageStatsService.this.queryEventsForPackage(callingUserId, beginTime,
- endTime, callingPackage);
+ endTime, callingPackage, includeTaskRoot);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -989,7 +1024,7 @@
final long token = Binder.clearCallingIdentity();
try {
return UsageStatsService.this.queryEventsForPackage(userId, beginTime,
- endTime, pkg);
+ endTime, pkg, true);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -1229,7 +1264,7 @@
public void registerAppUsageObserver(int observerId,
String[] packages, long timeLimitMs, PendingIntent
callbackIntent, String callingPackage) {
- if (!hasObserverPermission(callingPackage)) {
+ if (!hasObserverPermission()) {
throw new SecurityException("Caller doesn't have OBSERVE_APP_USAGE permission");
}
@@ -1252,7 +1287,7 @@
@Override
public void unregisterAppUsageObserver(int observerId, String callingPackage) {
- if (!hasObserverPermission(callingPackage)) {
+ if (!hasObserverPermission()) {
throw new SecurityException("Caller doesn't have OBSERVE_APP_USAGE permission");
}
@@ -1271,7 +1306,7 @@
long timeLimitMs, long sessionThresholdTimeMs,
PendingIntent limitReachedCallbackIntent, PendingIntent sessionEndCallbackIntent,
String callingPackage) {
- if (!hasObserverPermission(callingPackage)) {
+ if (!hasObserverPermission()) {
throw new SecurityException("Caller doesn't have OBSERVE_APP_USAGE permission");
}
@@ -1295,7 +1330,7 @@
@Override
public void unregisterUsageSessionObserver(int sessionObserverId, String callingPackage) {
- if (!hasObserverPermission(callingPackage)) {
+ if (!hasObserverPermission()) {
throw new SecurityException("Caller doesn't have OBSERVE_APP_USAGE permission");
}
@@ -1373,6 +1408,21 @@
Binder.restoreCallingIdentity(binderToken);
}
}
+
+ @Override
+ public @UsageSource int getUsageSource() {
+ if (!hasObserverPermission()) {
+ throw new SecurityException("Caller doesn't have OBSERVE_APP_USAGE permission");
+ }
+ synchronized (mLock) {
+ return mUsageSource;
+ }
+ }
+
+ @Override
+ public void forceUsageSourceSettingRead() {
+ readUsageSourceSetting();
+ }
}
void registerAppUsageObserver(int callingUid, int observerId, String[] packages,
@@ -1406,7 +1456,7 @@
@Override
public void reportEvent(ComponentName component, int userId, int eventType,
- int instanceId) {
+ int instanceId, ComponentName taskRoot) {
if (component == null) {
Slog.w(TAG, "Event reported without a component name");
return;
@@ -1416,6 +1466,13 @@
event.mPackage = component.getPackageName();
event.mClass = component.getClassName();
event.mInstanceId = instanceId;
+ if (taskRoot == null) {
+ event.mTaskRootPackage = null;
+ event.mTaskRootClass = null;
+ } else {
+ event.mTaskRootPackage = taskRoot.getPackageName();
+ event.mTaskRootClass = taskRoot.getClassName();
+ }
mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
}
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 2d1098c7..d52d32f 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -401,7 +401,7 @@
}
UsageEvents queryEvents(final long beginTime, final long endTime,
- boolean obfuscateInstantApps) {
+ boolean obfuscateInstantApps) {
final ArraySet<String> names = new ArraySet<>();
List<Event> results = queryStats(INTERVAL_DAILY,
beginTime, endTime, new StatCombiner<Event>() {
@@ -425,6 +425,12 @@
if (event.mClass != null) {
names.add(event.mClass);
}
+ if (event.mTaskRootPackage != null) {
+ names.add(event.mTaskRootPackage);
+ }
+ if (event.mTaskRootClass != null) {
+ names.add(event.mTaskRootClass);
+ }
accumulatedResult.add(event);
}
}
@@ -436,11 +442,11 @@
String[] table = names.toArray(new String[names.size()]);
Arrays.sort(table);
- return new UsageEvents(results, table);
+ return new UsageEvents(results, table, true);
}
UsageEvents queryEventsForPackage(final long beginTime, final long endTime,
- final String packageName) {
+ final String packageName, boolean includeTaskRoot) {
final ArraySet<String> names = new ArraySet<>();
names.add(packageName);
final List<Event> results = queryStats(INTERVAL_DAILY,
@@ -459,6 +465,12 @@
if (event.mClass != null) {
names.add(event.mClass);
}
+ if (includeTaskRoot && event.mTaskRootPackage != null) {
+ names.add(event.mTaskRootPackage);
+ }
+ if (includeTaskRoot && event.mTaskRootClass != null) {
+ names.add(event.mTaskRootClass);
+ }
accumulatedResult.add(event);
}
});
@@ -469,7 +481,7 @@
final String[] table = names.toArray(new String[names.size()]);
Arrays.sort(table);
- return new UsageEvents(results, table);
+ return new UsageEvents(results, table, includeTaskRoot);
}
void persistActiveStats() {
@@ -684,6 +696,14 @@
pw.printPair("instanceId", event.getInstanceId());
}
+ if (event.getTaskRootPackageName() != null) {
+ pw.printPair("taskRootPackage", event.getTaskRootPackageName());
+ }
+
+ if (event.getTaskRootClassName() != null) {
+ pw.printPair("taskRootClass", event.getTaskRootClassName());
+ }
+
if (event.mNotificationChannelId != null) {
pw.printPair("channelId", event.mNotificationChannelId);
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 312b318..a33b44c 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -2508,10 +2508,18 @@
public static final String KEY_GPS_LOCK_STRING = KEY_PREFIX + "gps_lock";
/**
- * SUPL NI emergency extension time in seconds. Default to "0".
+ * Control Plane / SUPL NI emergency extension time in seconds. Default to "0".
*/
public static final String KEY_ES_EXTENSION_SEC = KEY_PREFIX + "es_extension_sec";
+ /**
+ * Space separated list of Android package names of proxy applications representing
+ * the non-framework entities requesting location directly from GNSS without involving
+ * the framework, as managed by IGnssVisibilityControl.hal. For example,
+ * "com.example.mdt com.example.ims".
+ */
+ public static final String KEY_NFW_PROXY_APPS = KEY_PREFIX + "nfw_proxy_apps";
+
private static PersistableBundle getDefaults() {
PersistableBundle defaults = new PersistableBundle();
defaults.putBoolean(KEY_PERSIST_LPP_MODE_BOOL, true);
@@ -2525,6 +2533,7 @@
defaults.putString(KEY_A_GLONASS_POS_PROTOCOL_SELECT_STRING, "0");
defaults.putString(KEY_GPS_LOCK_STRING, "3");
defaults.putString(KEY_ES_EXTENSION_SEC, "0");
+ defaults.putString(KEY_NFW_PROXY_APPS, "");
return defaults;
}
}
diff --git a/telephony/java/android/telephony/CellSignalStrengthGsm.java b/telephony/java/android/telephony/CellSignalStrengthGsm.java
index 7b29f69..30e641d 100644
--- a/telephony/java/android/telephony/CellSignalStrengthGsm.java
+++ b/telephony/java/android/telephony/CellSignalStrengthGsm.java
@@ -148,8 +148,9 @@
/**
* Return the Bit Error Rate
- * @returns the bit error rate (0-7, 99) as defined in TS 27.007 8.5 or UNAVAILABLE.
- * @hide
+ *
+ * @return the bit error rate (0-7, 99) as defined in TS 27.007 8.5 or
+ * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE}.
*/
public int getBitErrorRate() {
return mBitErrorRate;
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index ad3ca6d..91375bc 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -292,14 +292,27 @@
* Asu is calculated based on 3GPP RSRP. Refer to 3GPP 27.007 (Ver 10.3.0) Sec 8.69
*
* @return RSSI in ASU 0..31, 99, or UNAVAILABLE
+ *
+ * @deprecated this information should be retrieved from
+ * {@link CellSignalStrengthGsm#getAsuLevel}.
+ * @see android.telephony#CellSignalStrengthGsm
+ * @see android.telephony.SignalStrength#getCellSignalStrengths
*/
+ @Deprecated
public int getGsmSignalStrength() {
return mGsm.getAsuLevel();
}
/**
* Get the GSM bit error rate (0-7, 99) as defined in TS 27.007 8.5
+ *
+ * @deprecated this information should be retrieved from
+ * {@link CellSignalStrengthGsm#getBitErrorRate}.
+ *
+ * @see android.telephony#CellSignalStrengthGsm
+ * @see android.telephony.SignalStrength#getCellSignalStrengths()
*/
+ @Deprecated
public int getGsmBitErrorRate() {
return mGsm.getBitErrorRate();
}
@@ -308,14 +321,28 @@
* Get the CDMA RSSI value in dBm
*
* @return the CDMA RSSI value or {@link #INVALID} if invalid
+ *
+ * @deprecated this information should be retrieved from
+ * {@link CellSignalStrengthCdma#getCdmaDbm}.
+ *
+ * @see android.telephony#CellSignalStrengthCdma
+ * @see android.telephony.SignalStrength#getCellSignalStrengths()
*/
+ @Deprecated
public int getCdmaDbm() {
return mCdma.getCdmaDbm();
}
/**
* Get the CDMA Ec/Io value in dB*10
+ *
+ * @deprecated this information should be retrieved from
+ * {@link CellSignalStrengthCdma#getCdmaEcio}.
+ *
+ * @see android.telephony#CellSignalStrengthCdma
+ * @see android.telephony.SignalStrength#getCellSignalStrengths()
*/
+ @Deprecated
public int getCdmaEcio() {
return mCdma.getCdmaEcio();
}
@@ -324,51 +351,112 @@
* Get the EVDO RSSI value in dBm
*
* @return the EVDO RSSI value or {@link #INVALID} if invalid
+ *
+ * @deprecated this information should be retrieved from
+ * {@link CellSignalStrengthCdma#getEvdoDbm}.
+ *
+ * @see android.telephony#CellSignalStrengthCdma
+ * @see android.telephony.SignalStrength#getCellSignalStrengths()
*/
+ @Deprecated
public int getEvdoDbm() {
return mCdma.getEvdoDbm();
}
/**
* Get the EVDO Ec/Io value in dB*10
+ *
+ * @deprecated this information should be retrieved from
+ * {@link CellSignalStrengthCdma#getEvdoEcio}.
+ *
+ * @see android.telephony#CellSignalStrengthCdma
+ * @see android.telephony.SignalStrength#getCellSignalStrengths()
*/
+ @Deprecated
public int getEvdoEcio() {
return mCdma.getEvdoEcio();
}
/**
* Get the signal to noise ratio. Valid values are 0-8. 8 is the highest.
+ *
+ * @deprecated this information should be retrieved from
+ * {@link CellSignalStrengthCdma#getEvdoSnr}.
+ *
+ * @see android.telephony#CellSignalStrengthCdma
+ * @see android.telephony.SignalStrength#getCellSignalStrengths()
*/
+ @Deprecated
public int getEvdoSnr() {
return mCdma.getEvdoSnr();
}
- /** @hide */
- @UnsupportedAppUsage
+ /**
+ * @deprecated this information should be retrieved from
+ * {@link CellSignalStrengthLte#getRssi}.
+ *
+ * @see android.telephony#CellSignalStrengthLte
+ * @see android.telephony.SignalStrength#getCellSignalStrengths()
+ * @hide
+ */
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public int getLteSignalStrength() {
return mLte.getRssi();
}
- /** @hide */
- @UnsupportedAppUsage
+ /**
+ * @deprecated this information should be retrieved from
+ * {@link CellSignalStrengthLte#getRsrp}.
+ *
+ * @see android.telephony#CellSignalStrengthLte
+ * @see android.telephony.SignalStrength#getCellSignalStrengths()
+ * @hide
+ */
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public int getLteRsrp() {
return mLte.getRsrp();
}
- /** @hide */
- @UnsupportedAppUsage
+ /**
+ * @deprecated this information should be retrieved from
+ * {@link CellSignalStrengthLte#getRsrq}.
+ *
+ * @see android.telephony#CellSignalStrengthLte
+ * @see android.telephony.SignalStrength#getCellSignalStrengths()
+ * @hide
+ */
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public int getLteRsrq() {
return mLte.getRsrq();
}
- /** @hide */
- @UnsupportedAppUsage
+ /**
+ * @deprecated this information should be retrieved from
+ * {@link CellSignalStrengthLte#getRssnr}.
+ *
+ * @see android.telephony#CellSignalStrengthLte
+ * @see android.telephony.SignalStrength#getCellSignalStrengths()
+ * @hide
+ */
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public int getLteRssnr() {
return mLte.getRssnr();
}
- /** @hide */
- @UnsupportedAppUsage
+ /**
+ * @deprecated this information should be retrieved from
+ * {@link CellSignalStrengthLte#getCqi}.
+ *
+ * @see android.telephony#CellSignalStrengthLte
+ * @see android.telephony.SignalStrength#getCellSignalStrengths()
+ * @hide
+ */
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public int getLteCqi() {
return mLte.getCqi();
}
@@ -391,11 +479,17 @@
}
/**
- * Get the signal level as an asu value between 0..31, 99 is unknown
+ * Get the signal level as an asu value with a range dependent on the underlying technology.
*
+ * @deprecated this information should be retrieved from
+ * {@link CellSignalStrength#getAsuLevel}. Because the levels vary by technology,
+ * this method is misleading and should not be used.
+ * @see android.telephony#CellSignalStrength
+ * @see android.telephony.SignalStrength#getCellSignalStrengths
* @hide
*/
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public int getAsuLevel() {
return getPrimary().getAsuLevel();
}
@@ -403,9 +497,15 @@
/**
* Get the signal strength as dBm
*
+ * @deprecated this information should be retrieved from
+ * {@link CellSignalStrength#getDbm()}. Because the levels vary by technology,
+ * this method is misleading and should not be used.
+ * @see android.telephony#CellSignalStrength
+ * @see android.telephony.SignalStrength#getCellSignalStrengths
* @hide
*/
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public int getDbm() {
return getPrimary().getDbm();
}
@@ -413,9 +513,15 @@
/**
* Get Gsm signal strength as dBm
*
+ * @deprecated this information should be retrieved from
+ * {@link CellSignalStrengthGsm#getDbm}.
+ *
+ * @see android.telephony#CellSignalStrengthGsm
+ * @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public int getGsmDbm() {
return mGsm.getDbm();
}
@@ -423,9 +529,15 @@
/**
* Get gsm as level 0..4
*
+ * @deprecated this information should be retrieved from
+ * {@link CellSignalStrengthGsm#getLevel}.
+ *
+ * @see android.telephony#CellSignalStrengthGsm
+ * @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public int getGsmLevel() {
return mGsm.getLevel();
}
@@ -433,9 +545,15 @@
/**
* Get the gsm signal level as an asu value between 0..31, 99 is unknown
*
+ * @deprecated this information should be retrieved from
+ * {@link CellSignalStrengthGsm#getAsuLevel}.
+ *
+ * @see android.telephony#CellSignalStrengthGsm
+ * @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public int getGsmAsuLevel() {
return mGsm.getAsuLevel();
}
@@ -443,9 +561,15 @@
/**
* Get cdma as level 0..4
*
+ * @deprecated this information should be retrieved from
+ * {@link CellSignalStrengthCdma#getLevel}.
+ *
+ * @see android.telephony#CellSignalStrengthCdma
+ * @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public int getCdmaLevel() {
return mCdma.getLevel();
}
@@ -453,9 +577,17 @@
/**
* Get the cdma signal level as an asu value between 0..31, 99 is unknown
*
+ * @deprecated this information should be retrieved from
+ * {@link CellSignalStrengthCdma#getAsuLevel}. Since there is no definition of
+ * ASU for CDMA, the resultant value is Android-specific and is not recommended
+ * for use.
+ *
+ * @see android.telephony#CellSignalStrengthCdma
+ * @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public int getCdmaAsuLevel() {
return mCdma.getAsuLevel();
}
@@ -463,9 +595,15 @@
/**
* Get Evdo as level 0..4
*
+ * @deprecated this information should be retrieved from
+ * {@link CellSignalStrengthCdma#getEvdoLevel}.
+ *
+ * @see android.telephony#CellSignalStrengthCdma
+ * @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public int getEvdoLevel() {
return mCdma.getEvdoLevel();
}
@@ -473,9 +611,17 @@
/**
* Get the evdo signal level as an asu value between 0..31, 99 is unknown
*
+ * @deprecated this information should be retrieved from
+ * {@link CellSignalStrengthCdma#getEvdoAsuLevel}. Since there is no definition of
+ * ASU for EvDO, the resultant value is Android-specific and is not recommended
+ * for use.
+ *
+ * @see android.telephony#CellSignalStrengthCdma
+ * @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public int getEvdoAsuLevel() {
return mCdma.getEvdoAsuLevel();
}
@@ -483,9 +629,15 @@
/**
* Get LTE as dBm
*
+ * @deprecated this information should be retrieved from
+ * {@link CellSignalStrengthLte#getDbm}.
+ *
+ * @see android.telephony#CellSignalStrengthLte
+ * @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public int getLteDbm() {
return mLte.getRsrp();
}
@@ -493,9 +645,15 @@
/**
* Get LTE as level 0..4
*
+ * @deprecated this information should be retrieved from
+ * {@link CellSignalStrengthLte#getLevel}.
+ *
+ * @see android.telephony#CellSignalStrengthLte
+ * @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public int getLteLevel() {
return mLte.getLevel();
}
@@ -504,26 +662,46 @@
* Get the LTE signal level as an asu value between 0..97, 99 is unknown
* Asu is calculated based on 3GPP RSRP. Refer to 3GPP 27.007 (Ver 10.3.0) Sec 8.69
*
+ * @deprecated this information should be retrieved from
+ * {@link CellSignalStrengthLte#getAsuLevel}.
+ *
+ * @see android.telephony#CellSignalStrengthLte
+ * @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public int getLteAsuLevel() {
return mLte.getAsuLevel();
}
/**
* @return true if this is for GSM
+ *
+ * @deprecated This method returns true if there are any 3gpp type SignalStrength elements in
+ * this SignalStrength report or if the report contains no valid SignalStrength
+ * information. Instead callers should use
+ * {@link android.telephony.SignalStrength#getCellSignalStrengths
+ * getCellSignalStrengths()} to determine which types of information are contained
+ * in the SignalStrength report.
*/
+ @Deprecated
public boolean isGsm() {
return !(getPrimary() instanceof CellSignalStrengthCdma);
}
/**
- * @return get TD_SCDMA dbm
+ * @return get TD-SCDMA dBm
*
+ * @deprecated this information should be retrieved from
+ * {@link CellSignalStrengthTdscdma#getDbm}.
+ *
+ * @see android.telephony#CellSignalStrengthTdscdma
+ * @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public int getTdScdmaDbm() {
return mTdscdma.getRscp();
}
@@ -534,9 +712,15 @@
* INT_MAX: 0x7FFFFFFF denotes invalid value
* Reference: 3GPP TS 25.123, section 9.1.1.1
*
+ * @deprecated this information should be retrieved from
+ * {@link CellSignalStrengthTdscdma#getLevel}.
+ *
+ * @see android.telephony#CellSignalStrengthTdscdma
+ * @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public int getTdScdmaLevel() {
return mTdscdma.getLevel();
}
@@ -544,18 +728,30 @@
/**
* Get the TD-SCDMA signal level as an asu value.
*
+ * @deprecated this information should be retrieved from
+ * {@link CellSignalStrengthTdscdma#getAsuLevel}.
+ *
+ * @see android.telephony#CellSignalStrengthTdscdma
+ * @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public int getTdScdmaAsuLevel() {
return mTdscdma.getAsuLevel();
}
/**
- * Gets WCDMA RSCP as a dbm value between -120 and -24, as defined in TS 27.007 8.69.
+ * Gets WCDMA RSCP as a dBm value between -120 and -24, as defined in TS 27.007 8.69.
*
+ * @deprecated this information should be retrieved from
+ * {@link CellSignalStrengthWcdma#getRscp}.
+ *
+ * @see android.telephony#CellSignalStrengthWcdma
+ * @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
+ @Deprecated
public int getWcdmaRscp() {
return mWcdma.getRscp();
}
@@ -563,8 +759,14 @@
/**
* Get the WCDMA signal level as an ASU value between 0-96, 255 is unknown
*
+ * @deprecated this information should be retrieved from
+ * {@link CellSignalStrengthWcdma#getAsuLevel}.
+ *
+ * @see android.telephony#CellSignalStrengthWcdma
+ * @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
+ @Deprecated
public int getWcdmaAsuLevel() {
/*
* 3GPP 27.007 (Ver 10.3.0) Sec 8.69
@@ -578,10 +780,16 @@
}
/**
- * Gets WCDMA signal strength as a dbm value between -120 and -24, as defined in TS 27.007 8.69.
+ * Gets WCDMA signal strength as a dBm value between -120 and -24, as defined in TS 27.007 8.69.
*
+ * @deprecated this information should be retrieved from
+ * {@link CellSignalStrengthWcdma#getDbm}.
+ *
+ * @see android.telephony#CellSignalStrengthWcdma
+ * @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
+ @Deprecated
public int getWcdmaDbm() {
return mWcdma.getDbm();
}
@@ -589,13 +797,19 @@
/**
* Get WCDMA as level 0..4
*
+ * @deprecated this information should be retrieved from
+ * {@link CellSignalStrengthWcdma#getDbm}.
+ *
+ * @see android.telephony#CellSignalStrengthWcdma
+ * @see android.telephony.SignalStrength#getCellSignalStrengths()
* @hide
*/
+ @Deprecated
public int getWcdmaLevel() {
return mWcdma.getLevel();
}
- /**
+ /**
* @return hash code
*/
@Override
@@ -639,9 +853,13 @@
* Set SignalStrength based on intent notifier map
*
* @param m intent notifier map
+ *
+ * @deprecated this method relies on non-stable implementation details, and full access to
+ * internal storage is available via {@link getCellSignalStrengths()}.
* @hide
*/
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private void setFromNotifierBundle(Bundle m) {
mCdma = m.getParcelable("Cdma");
mGsm = m.getParcelable("Gsm");
@@ -654,9 +872,13 @@
* Set intent notifier Bundle based on SignalStrength
*
* @param m intent notifier Bundle
+ *
+ * @deprecated this method relies on non-stable implementation details, and full access to
+ * internal storage is available via {@link getCellSignalStrengths()}.
* @hide
*/
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public void fillInNotifierBundle(Bundle m) {
m.putParcelable("Cdma", mCdma);
m.putParcelable("Gsm", mGsm);
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 8053353..e710e0e 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -93,6 +93,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.regex.Matcher;
@@ -8585,12 +8586,25 @@
}
- /** @hide */
- public String getLocaleFromDefaultSim() {
+ /**
+ * Returns a well-formed IETF BCP 47 language tag representing the locale from the SIM, e.g,
+ * en-US. Returns {@code null} if no locale could be derived from subscriptions.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
+ *
+ * @see Locale#toLanguageTag()
+ * @see Locale#forLanguageTag(String)
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @Nullable public String getSimLocale() {
try {
final ITelephony telephony = getITelephony();
if (telephony != null) {
- return telephony.getLocaleFromDefaultSim();
+ return telephony.getSimLocaleForSubscriber(getSubId());
}
} catch (RemoteException ex) {
}
@@ -8598,6 +8612,22 @@
}
/**
+ * TODO delete after SuW migrates to new API.
+ * @hide
+ */
+ public String getLocaleFromDefaultSim() {
+ try {
+ final ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony.getSimLocaleForSubscriber(getSubId());
+ }
+ } catch (RemoteException ex) {
+ }
+ return null;
+ }
+
+
+ /**
* Requests the modem activity info. The recipient will place the result
* in `result`.
* @param result The object on which the recipient will send the resulting
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 5736a46..9cc173c 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1178,12 +1178,12 @@
void factoryReset(int subId);
/**
- * An estimate of the users's current locale based on the default SIM.
+ * Returns users's current locale based on the SIM.
*
* The returned string will be a well formed BCP-47 language tag, or {@code null}
* if no locale could be derived.
*/
- String getLocaleFromDefaultSim();
+ String getSimLocaleForSubscriber(int subId);
/**
* Requests the modem activity info asynchronously.
diff --git a/tests/HwAccelerationTest/Android.mk b/tests/HwAccelerationTest/Android.mk
index 11ea954..79072fa 100644
--- a/tests/HwAccelerationTest/Android.mk
+++ b/tests/HwAccelerationTest/Android.mk
@@ -21,6 +21,7 @@
LOCAL_PACKAGE_NAME := HwAccelerationTest
LOCAL_PRIVATE_PLATFORM_APIS := true
+LOCAL_CERTIFICATE := platform
LOCAL_MODULE_TAGS := tests
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index c8f96c9..f330b83 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -310,6 +310,15 @@
<category android:name="com.android.test.hwui.TEST" />
</intent-filter>
</activity>
+
+ <activity
+ android:name="PictureCaptureDemo"
+ android:label="Debug/Picture Capture">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="com.android.test.hwui.TEST" />
+ </intent-filter>
+ </activity>
<activity
android:name="SmallCircleActivity"
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/PictureCaptureDemo.java b/tests/HwAccelerationTest/src/com/android/test/hwui/PictureCaptureDemo.java
new file mode 100644
index 0000000..029e302
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/PictureCaptureDemo.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Picture;
+import android.os.Bundle;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.ViewDebug;
+import android.webkit.WebChromeClient;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.LinearLayout.LayoutParams;
+import android.widget.ProgressBar;
+
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+import java.util.Random;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+public class PictureCaptureDemo extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ final LinearLayout layout = new LinearLayout(this);
+ layout.setOrientation(LinearLayout.VERTICAL);
+
+ final LinearLayout inner = new LinearLayout(this);
+ inner.setOrientation(LinearLayout.HORIZONTAL);
+ ProgressBar spinner = new ProgressBar(this, null, android.R.attr.progressBarStyleLarge);
+ inner.addView(spinner,
+ new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
+
+ inner.addView(new View(this), new LayoutParams(50, 1));
+
+ Picture picture = new Picture();
+ Canvas canvas = picture.beginRecording(100, 100);
+ canvas.drawColor(Color.RED);
+ Paint paint = new Paint();
+ paint.setTextSize(32);
+ paint.setColor(Color.BLACK);
+ canvas.drawText("Hello", 0, 50, paint);
+ picture.endRecording();
+
+ ImageView iv1 = new ImageView(this);
+ iv1.setImageBitmap(Bitmap.createBitmap(picture, 100, 100, Bitmap.Config.ARGB_8888));
+ inner.addView(iv1, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
+
+ inner.addView(new View(this), new LayoutParams(50, 1));
+
+ ImageView iv2 = new ImageView(this);
+ iv2.setImageBitmap(Bitmap.createBitmap(picture, 100, 100, Bitmap.Config.HARDWARE));
+ inner.addView(iv2, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
+
+ layout.addView(inner,
+ new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
+ // For testing with a functor in the tree
+ WebView wv = new WebView(this);
+ wv.setWebViewClient(new WebViewClient());
+ wv.setWebChromeClient(new WebChromeClient());
+ wv.loadUrl("https://google.com");
+ layout.addView(wv, new LayoutParams(LayoutParams.MATCH_PARENT, 400));
+
+ SurfaceView mySurfaceView = new SurfaceView(this);
+ layout.addView(mySurfaceView,
+ new LayoutParams(LayoutParams.MATCH_PARENT, 600));
+
+ setContentView(layout);
+
+ mySurfaceView.getHolder().addCallback(new SurfaceHolder.Callback() {
+ private AutoCloseable mStopCapture;
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ final Random rand = new Random();
+ mStopCapture = ViewDebug.startRenderingCommandsCapture(mySurfaceView,
+ mCaptureThread, (picture) -> {
+ if (rand.nextInt(20) == 0) {
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ }
+ }
+ Canvas canvas = holder.lockCanvas();
+ if (canvas == null) {
+ return false;
+ }
+ canvas.drawPicture(picture);
+ holder.unlockCanvasAndPost(canvas);
+ picture.close();
+ return true;
+ });
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ if (mStopCapture != null) {
+ try {
+ mStopCapture.close();
+ } catch (Exception e) {
+ }
+ mStopCapture = null;
+ }
+ }
+ });
+ }
+
+ ExecutorService mCaptureThread = Executors.newSingleThreadExecutor();
+ ExecutorService mExecutor = Executors.newSingleThreadExecutor();
+
+ Picture deepCopy(Picture src) {
+ try {
+ PipedInputStream inputStream = new PipedInputStream();
+ PipedOutputStream outputStream = new PipedOutputStream(inputStream);
+ Future<Picture> future = mExecutor.submit(() -> Picture.createFromStream(inputStream));
+ src.writeToStream(outputStream);
+ outputStream.close();
+ return future.get();
+ } catch (Exception ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+}
diff --git a/tests/net/Android.mk b/tests/net/Android.mk
index 6850673..7e1b400 100644
--- a/tests/net/Android.mk
+++ b/tests/net/Android.mk
@@ -32,74 +32,6 @@
LOCAL_CERTIFICATE := platform
-# These are not normally accessible from apps so they must be explicitly included.
-LOCAL_JNI_SHARED_LIBRARIES := \
- android.hidl.token@1.0 \
- libartbase \
- libbacktrace \
- libbase \
- libbinder \
- libbinderthreadstate \
- libc++ \
- libcrypto \
- libcutils \
- libdexfile \
- libframeworksnettestsjni \
- libhidl-gen-utils \
- libhidlbase \
- libhidltransport \
- libhwbinder \
- liblog \
- liblzma \
- libnativehelper \
- libpackagelistparser \
- libpcre2 \
- libprocessgroup \
- libselinux \
- libui \
- libutils \
- libvintf \
- libvndksupport \
- libtinyxml2 \
- libunwindstack \
- libutilscallstack \
- libziparchive \
- libz \
- netd_aidl_interface-cpp
-
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
include $(BUILD_PACKAGE)
-
-#########################################################################
-# Build JNI Shared Library
-#########################################################################
-
-LOCAL_PATH:= $(LOCAL_PATH)/jni
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_CFLAGS := -Wall -Wextra -Werror
-
-LOCAL_C_INCLUDES := \
- libpcap \
- hardware/google/apf
-
-LOCAL_SRC_FILES := $(call all-cpp-files-under)
-
-LOCAL_SHARED_LIBRARIES := \
- libbinder \
- liblog \
- libcutils \
- libnativehelper \
- netd_aidl_interface-cpp
-
-LOCAL_STATIC_LIBRARIES := \
- libpcap \
- libapf
-
-LOCAL_MODULE := libframeworksnettestsjni
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 1c26418..dda4481 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -1508,6 +1508,12 @@
verifyActiveNetwork(TRANSPORT_WIFI);
}
+ @Test
+ public void testRequiresValidation() {
+ assertTrue(NetworkMonitorUtils.isValidationRequired(
+ mCm.getDefaultRequest().networkCapabilities));
+ }
+
enum CallbackState {
NONE,
AVAILABLE,
@@ -4404,8 +4410,7 @@
mMockVpn.setUids(ranges);
// VPN networks do not satisfy the default request and are automatically validated
// by NetworkMonitor
- assertFalse(NetworkMonitorUtils.isValidationRequired(
- mCm.getDefaultRequest().networkCapabilities, vpnNetworkAgent.mNetworkCapabilities));
+ assertFalse(NetworkMonitorUtils.isValidationRequired(vpnNetworkAgent.mNetworkCapabilities));
vpnNetworkAgent.setNetworkValid();
vpnNetworkAgent.connect(false);