Merge "Introduce a new output stream that is capable of tracking the number of bytes that are written to the output stream."
diff --git a/core/java/android/content/pm/parsing/AndroidPackage.java b/core/java/android/content/pm/parsing/AndroidPackage.java
index 35df474..0562dff 100644
--- a/core/java/android/content/pm/parsing/AndroidPackage.java
+++ b/core/java/android/content/pm/parsing/AndroidPackage.java
@@ -403,6 +403,8 @@
boolean isEnabled();
+ boolean isCrossProfile();
+
boolean isEncryptionAware();
boolean isExternal();
diff --git a/core/java/android/content/pm/parsing/ApkParseUtils.java b/core/java/android/content/pm/parsing/ApkParseUtils.java
index 7732316..3f22967 100644
--- a/core/java/android/content/pm/parsing/ApkParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkParseUtils.java
@@ -2070,6 +2070,9 @@
sa.getBoolean(R.styleable.AndroidManifestApplication_enabled,
true));
+ parsingPackage.setCrossProfile(
+ sa.getBoolean(R.styleable.AndroidManifestApplication_crossProfile, false));
+
parsingPackage.setIsGame(sa.getBoolean(
R.styleable.AndroidManifestApplication_isGame, false));
diff --git a/core/java/android/content/pm/parsing/PackageImpl.java b/core/java/android/content/pm/parsing/PackageImpl.java
index 0e736d5..18dee23 100644
--- a/core/java/android/content/pm/parsing/PackageImpl.java
+++ b/core/java/android/content/pm/parsing/PackageImpl.java
@@ -240,6 +240,7 @@
private int descriptionRes;
private String deviceProtectedDataDir;
private boolean enabled;
+ private boolean crossProfile;
private int flags;
private int fullBackupContent;
private boolean hiddenUntilInstalled;
@@ -1636,6 +1637,12 @@
}
@Override
+ public PackageImpl setCrossProfile(boolean crossProfile) {
+ this.crossProfile = crossProfile;
+ return this;
+ }
+
+ @Override
public PackageImpl setUiOptions(int uiOptions) {
this.uiOptions = uiOptions;
return this;
@@ -2835,6 +2842,11 @@
}
@Override
+ public boolean isCrossProfile() {
+ return crossProfile;
+ }
+
+ @Override
public String getManageSpaceActivityName() {
return manageSpaceActivityName;
}
@@ -3042,6 +3054,7 @@
dest.writeInt(this.descriptionRes);
dest.writeString(this.deviceProtectedDataDir);
dest.writeBoolean(this.enabled);
+ dest.writeBoolean(this.crossProfile);
dest.writeInt(this.flags);
dest.writeInt(this.fullBackupContent);
dest.writeBoolean(this.hiddenUntilInstalled);
@@ -3194,6 +3207,7 @@
this.descriptionRes = in.readInt();
this.deviceProtectedDataDir = in.readString();
this.enabled = in.readBoolean();
+ this.crossProfile = in.readBoolean();
this.flags = in.readInt();
this.fullBackupContent = in.readInt();
this.hiddenUntilInstalled = in.readBoolean();
diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java
index aff1b2e..47dac55 100644
--- a/core/java/android/content/pm/parsing/ParsingPackage.java
+++ b/core/java/android/content/pm/parsing/ParsingPackage.java
@@ -229,6 +229,8 @@
ParsingPackage setEnabled(boolean enabled);
+ ParsingPackage setCrossProfile(boolean crossProfile);
+
ParsingPackage setFullBackupContent(int fullBackupContent);
ParsingPackage setHasDomainUrls(boolean hasDomainUrls);
diff --git a/core/java/android/service/controls/Control.java b/core/java/android/service/controls/Control.java
index df18eb6..43a308c 100644
--- a/core/java/android/service/controls/Control.java
+++ b/core/java/android/service/controls/Control.java
@@ -16,6 +16,7 @@
package android.service.controls;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.PendingIntent;
@@ -28,28 +29,26 @@
import com.android.internal.util.Preconditions;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* Represents a physical object that can be represented by a {@link ControlTemplate} and whose
* properties may be modified through a {@link ControlAction}.
*
- * The information is provided by a {@link ControlProviderService} and represents static
+ * The information is provided by a {@link ControlsProviderService} and represents static
* information (not current status) about the device.
* <p>
* Each control needs a unique (per provider) identifier that is persistent across reboots of the
* system.
* <p>
* Each {@link Control} will have a name, a subtitle and will optionally belong to a structure
- * and zone. Some of these values are defined by the user and/or the {@link ControlProviderService}
+ * and zone. Some of these values are defined by the user and/or the {@link ControlsProviderService}
* and will be used to display the control as well as group them for management.
* <p>
* Each object will have an associated {@link DeviceTypes.DeviceType}. This will determine the icons and colors
* used to display it.
* <p>
- * The {@link ControlTemplate.TemplateType} provided will be used as a hint when displaying this in
- * non-interactive situations (for example when there's no state to display). This template is not
- * the one that will be shown with the current state and provide interactions. That template is set
- * using {@link ControlState}.
- * <p>
* An {@link Intent} linking to the provider Activity that expands on this {@link Control} and
* allows for further actions should be provided.
* @hide
@@ -57,17 +56,52 @@
public class Control implements Parcelable {
private static final String TAG = "Control";
- ;
+ private static final int NUM_STATUS = 5;
+ /**
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ STATUS_UNKNOWN,
+ STATUS_OK,
+ STATUS_NOT_FOUND,
+ STATUS_ERROR,
+ STATUS_DISABLED,
+ })
+ public @interface Status {};
+
+ public static final int STATUS_UNKNOWN = 0;
+
+ /**
+ * The device corresponding to the {@link Control} is responding correctly.
+ */
+ public static final int STATUS_OK = 1;
+
+ /**
+ * The device corresponding to the {@link Control} cannot be found or was removed.
+ */
+ public static final int STATUS_NOT_FOUND = 2;
+
+ /**
+ * The device corresponding to the {@link Control} is in an error state.
+ */
+ public static final int STATUS_ERROR = 3;
+
+ /**
+ * The {@link Control} is currently disabled.
+ */
+ public static final int STATUS_DISABLED = 4;
private final @NonNull String mControlId;
- private final @DeviceTypes.DeviceType
- int mDeviceType;
+ private final @DeviceTypes.DeviceType int mDeviceType;
private final @NonNull CharSequence mTitle;
private final @NonNull CharSequence mSubtitle;
private final @Nullable CharSequence mStructure;
private final @Nullable CharSequence mZone;
private final @NonNull PendingIntent mAppIntent;
- private final @ControlTemplate.TemplateType int mPrimaryType;
+ private final @Status int mStatus;
+ private final @NonNull ControlTemplate mControlTemplate;
+ private final @NonNull CharSequence mStatusText;
/**
* @param controlId the unique persistent identifier for this object.
@@ -79,7 +113,6 @@
* @param zone
* @param appIntent a {@link PendingIntent} linking to a page to interact with the
* corresponding device.
- * @param primaryType the primary template for this type.
*/
public Control(@NonNull String controlId,
@DeviceTypes.DeviceType int deviceType,
@@ -88,11 +121,15 @@
@Nullable CharSequence structure,
@Nullable CharSequence zone,
@NonNull PendingIntent appIntent,
- int primaryType) {
+ @Status int status,
+ @NonNull ControlTemplate controlTemplate,
+ @NonNull CharSequence statusText) {
Preconditions.checkNotNull(controlId);
Preconditions.checkNotNull(title);
Preconditions.checkNotNull(subtitle);
Preconditions.checkNotNull(appIntent);
+ Preconditions.checkNotNull(controlTemplate);
+ Preconditions.checkNotNull(statusText);
mControlId = controlId;
if (!DeviceTypes.validDeviceType(deviceType)) {
Log.e(TAG, "Invalid device type:" + deviceType);
@@ -105,7 +142,14 @@
mStructure = structure;
mZone = zone;
mAppIntent = appIntent;
- mPrimaryType = primaryType;
+ if (status < 0 || status >= NUM_STATUS) {
+ mStatus = STATUS_UNKNOWN;
+ Log.e(TAG, "Status unknown:" + status);
+ } else {
+ mStatus = status;
+ }
+ mControlTemplate = controlTemplate;
+ mStatusText = statusText;
}
public Control(Parcel in) {
@@ -124,7 +168,9 @@
mZone = null;
}
mAppIntent = PendingIntent.CREATOR.createFromParcel(in);
- mPrimaryType = in.readInt();
+ mStatus = in.readInt();
+ mControlTemplate = ControlTemplate.CREATOR.createFromParcel(in);
+ mStatusText = in.readCharSequence();
}
@NonNull
@@ -162,9 +208,19 @@
return mAppIntent;
}
- @android.service.controls.templates.ControlTemplate.TemplateType
- public int getPrimaryType() {
- return mPrimaryType;
+ @Status
+ public int getStatus() {
+ return mStatus;
+ }
+
+ @NonNull
+ public ControlTemplate getControlTemplate() {
+ return mControlTemplate;
+ }
+
+ @NonNull
+ public CharSequence getStatusText() {
+ return mStatusText;
}
@Override
@@ -191,7 +247,9 @@
dest.writeByte((byte) 0);
}
mAppIntent.writeToParcel(dest, flags);
- dest.writeInt(mPrimaryType);
+ dest.writeInt(mStatus);
+ mControlTemplate.writeToParcel(dest, flags);
+ dest.writeCharSequence(mStatusText);
}
public static final Creator<Control> CREATOR = new Creator<Control>() {
@@ -209,32 +267,39 @@
/**
* Builder class for {@link Control}.
*
- * This class facilitates the creation of {@link Control}. It provides the following
- * defaults for non-optional parameters:
+ * This class facilitates the creation of {@link Control} with no state.
+ * It provides the following defaults for non-optional parameters:
* <ul>
* <li> Device type: {@link DeviceTypes#TYPE_UNKNOWN}
* <li> Title: {@code ""}
* <li> Subtitle: {@code ""}
- * <li> Primary template: {@link ControlTemplate#TYPE_NONE}
+ * </ul>
+ * This fixes the values relating to state of the {@link Control} as required by
+ * {@link ControlsProviderService#onLoad}:
+ * <ul>
+ * <li> Status: {@link Status#STATUS_UNKNOWN}
+ * <li> Control template: {@link ControlTemplate#NO_TEMPLATE}
+ * <li> Status text: {@code ""}
* </ul>
*/
- public static class Builder {
- private static final String TAG = "Control.Builder";
- private @NonNull String mControlId;
- private @DeviceTypes.DeviceType
- int mDeviceType = DeviceTypes.TYPE_UNKNOWN;
- private @NonNull CharSequence mTitle = "";
- private @NonNull CharSequence mSubtitle = "";
- private @Nullable CharSequence mStructure;
- private @Nullable CharSequence mZone;
- private @NonNull PendingIntent mAppIntent;
- private @ControlTemplate.TemplateType int mPrimaryType = ControlTemplate.TYPE_NONE;
+ public static class StatelessBuilder {
+ private static final String TAG = "StatelessBuilder";
+ protected @NonNull String mControlId;
+ protected @DeviceTypes.DeviceType int mDeviceType = DeviceTypes.TYPE_UNKNOWN;
+ protected @NonNull CharSequence mTitle = "";
+ protected @NonNull CharSequence mSubtitle = "";
+ protected @Nullable CharSequence mStructure;
+ protected @Nullable CharSequence mZone;
+ protected @NonNull PendingIntent mAppIntent;
+ protected @Status int mStatus = STATUS_UNKNOWN;
+ protected @NonNull ControlTemplate mControlTemplate = ControlTemplate.NO_TEMPLATE;
+ protected @NonNull CharSequence mStatusText = "";
/**
* @param controlId the identifier for the {@link Control}.
* @param appIntent the pending intent linking to the device Activity.
*/
- public Builder(@NonNull String controlId,
+ public StatelessBuilder(@NonNull String controlId,
@NonNull PendingIntent appIntent) {
Preconditions.checkNotNull(controlId);
Preconditions.checkNotNull(appIntent);
@@ -243,10 +308,10 @@
}
/**
- * Creates a {@link Builder} using an existing {@link Control} as a base.
+ * Creates a {@link StatelessBuilder} using an existing {@link Control} as a base.
* @param control base for the builder.
*/
- public Builder(@NonNull Control control) {
+ public StatelessBuilder(@NonNull Control control) {
Preconditions.checkNotNull(control);
mControlId = control.mControlId;
mDeviceType = control.mDeviceType;
@@ -255,7 +320,6 @@
mStructure = control.mStructure;
mZone = control.mZone;
mAppIntent = control.mAppIntent;
- mPrimaryType = control.mPrimaryType;
}
/**
@@ -263,14 +327,14 @@
* @return {@code this}
*/
@NonNull
- public Builder setControlId(@NonNull String controlId) {
+ public StatelessBuilder setControlId(@NonNull String controlId) {
Preconditions.checkNotNull(controlId);
mControlId = controlId;
return this;
}
@NonNull
- public Builder setDeviceType(@DeviceTypes.DeviceType int deviceType) {
+ public StatelessBuilder setDeviceType(@DeviceTypes.DeviceType int deviceType) {
if (!DeviceTypes.validDeviceType(deviceType)) {
Log.e(TAG, "Invalid device type:" + deviceType);
mDeviceType = DeviceTypes.TYPE_UNKNOWN;
@@ -285,27 +349,27 @@
* @return {@code this}
*/
@NonNull
- public Builder setTitle(@NonNull CharSequence title) {
+ public StatelessBuilder setTitle(@NonNull CharSequence title) {
Preconditions.checkNotNull(title);
mTitle = title;
return this;
}
@NonNull
- public Builder setSubtitle(@NonNull CharSequence subtitle) {
+ public StatelessBuilder setSubtitle(@NonNull CharSequence subtitle) {
Preconditions.checkNotNull(subtitle);
mSubtitle = subtitle;
return this;
}
@NonNull
- public Builder setStructure(@Nullable CharSequence structure) {
+ public StatelessBuilder setStructure(@Nullable CharSequence structure) {
mStructure = structure;
return this;
}
@NonNull
- public Builder setZone(@Nullable CharSequence zone) {
+ public StatelessBuilder setZone(@Nullable CharSequence zone) {
mZone = zone;
return this;
}
@@ -315,23 +379,13 @@
* @return {@code this}
*/
@NonNull
- public Builder setAppIntent(@NonNull PendingIntent appIntent) {
+ public StatelessBuilder setAppIntent(@NonNull PendingIntent appIntent) {
Preconditions.checkNotNull(appIntent);
mAppIntent = appIntent;
return this;
}
/**
- * @param type type to use as default in the {@link Control}
- * @return {@code this}
- */
- @NonNull
- public Builder setPrimaryType(@ControlTemplate.TemplateType int type) {
- mPrimaryType = type;
- return this;
- }
-
- /**
* Build a {@link Control}
* @return a valid {@link Control}
*/
@@ -344,7 +398,108 @@
mStructure,
mZone,
mAppIntent,
- mPrimaryType);
+ mStatus,
+ mControlTemplate,
+ mStatusText);
+ }
+ }
+
+ public static class StatefulBuilder extends StatelessBuilder {
+ private static final String TAG = "StatefulBuilder";
+
+ /**
+ * @param controlId the identifier for the {@link Control}.
+ * @param appIntent the pending intent linking to the device Activity.
+ */
+ public StatefulBuilder(@NonNull String controlId,
+ @NonNull PendingIntent appIntent) {
+ super(controlId, appIntent);
+ }
+
+ public StatefulBuilder(@NonNull Control control) {
+ super(control);
+ mStatus = control.mStatus;
+ mControlTemplate = control.mControlTemplate;
+ mStatusText = control.mStatusText;
+ }
+
+ /**
+ * @param controlId the identifier for the {@link Control}.
+ * @return {@code this}
+ */
+ @NonNull
+ public StatefulBuilder setControlId(@NonNull String controlId) {
+ super.setControlId(controlId);
+ return this;
+ }
+
+ @NonNull
+ public StatefulBuilder setDeviceType(@DeviceTypes.DeviceType int deviceType) {
+ super.setDeviceType(deviceType);
+ return this;
+ }
+
+ /**
+ * @param title the user facing name of the {@link Control}
+ * @return {@code this}
+ */
+ @NonNull
+ public StatefulBuilder setTitle(@NonNull CharSequence title) {
+ super.setTitle(title);
+ return this;
+ }
+
+ @NonNull
+ public StatefulBuilder setSubtitle(@NonNull CharSequence subtitle) {
+ super.setSubtitle(subtitle);
+ return this;
+ }
+
+ @NonNull
+ public StatefulBuilder setStructure(@Nullable CharSequence structure) {
+ super.setStructure(structure);
+ return this;
+ }
+
+ @NonNull
+ public StatefulBuilder setZone(@Nullable CharSequence zone) {
+ super.setZone(zone);
+ return this;
+ }
+
+ /**
+ * @param appIntent an {@link Intent} linking to an Activity for the {@link Control}
+ * @return {@code this}
+ */
+ @NonNull
+ public StatefulBuilder setAppIntent(@NonNull PendingIntent appIntent) {
+ super.setAppIntent(appIntent);
+ return this;
+ }
+
+ @NonNull
+ public StatefulBuilder setStatus(@Status int status) {
+ if (status < 0 || status >= NUM_STATUS) {
+ mStatus = STATUS_UNKNOWN;
+ Log.e(TAG, "Status unknown:" + status);
+ } else {
+ mStatus = status;
+ }
+ return this;
+ }
+
+ @NonNull
+ public StatefulBuilder setControlTemplate(@NonNull ControlTemplate controlTemplate) {
+ Preconditions.checkNotNull(controlTemplate);
+ mControlTemplate = controlTemplate;
+ return this;
+ }
+
+ @NonNull
+ public StatefulBuilder setStatusText(@NonNull CharSequence statusText) {
+ Preconditions.checkNotNull(statusText);
+ mStatusText = statusText;
+ return this;
}
}
}
diff --git a/core/java/android/service/controls/ControlState.aidl b/core/java/android/service/controls/ControlState.aidl
deleted file mode 100644
index 520d85b..0000000
--- a/core/java/android/service/controls/ControlState.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * 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 android.service.controls;
-
-parcelable ControlState;
\ No newline at end of file
diff --git a/core/java/android/service/controls/ControlState.java b/core/java/android/service/controls/ControlState.java
deleted file mode 100644
index 998fb54..0000000
--- a/core/java/android/service/controls/ControlState.java
+++ /dev/null
@@ -1,335 +0,0 @@
-/*
- * 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 android.service.controls;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.PendingIntent;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.service.controls.templates.ControlTemplate;
-import android.util.Log;
-
-import com.android.internal.util.Preconditions;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Current state for a {@link Control}.
- *
- * Collects information to render the current state of a {@link Control} as well as possible action
- * that can be performed on it.
- * <p>
- * Additionally, this object is used to modify elements from the {@link Control} such as device
- * type, and intents. This information will last until it is again modified by a
- * {@link ControlState}.
- * @hide
- */
-public final class ControlState implements Parcelable {
-
- private static final String TAG = "ControlState";
-
- /**
- * @hide
- */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({
- STATUS_OK,
- STATUS_NOT_FOUND,
- STATUS_ERROR,
- STATUS_DISABLED,
- })
- public @interface Status {};
-
- /**
- * The device corresponding to the {@link Control} is responding correctly.
- */
- public static final int STATUS_OK = 0;
-
- /**
- * The device corresponding to the {@link Control} cannot be found or was removed.
- */
- public static final int STATUS_NOT_FOUND = 1;
-
- /**
- * The device corresponding to the {@link Control} is in an error state.
- */
- public static final int STATUS_ERROR = 2;
-
- /**
- * The {@link Control} is currently disabled.
- */
- public static final int STATUS_DISABLED = 3;
-
- private final @NonNull String mControlId;
- private final @DeviceTypes.DeviceType int mDeviceType;
- private final @Status int mStatus;
- private final @NonNull ControlTemplate mControlTemplate;
- private final @NonNull CharSequence mStatusText;
- private final @Nullable PendingIntent mAppIntent;
-
- /**
- * @param controlId the identifier of the {@link Control} this object refers to.
- * @param status the current status of the {@link Control}.
- * @param deviceType the {@link DeviceTypes.DeviceType} to replace the one set in the
- * {@link Control} or set in the last {@link ControlState}. In order to keep
- * the current device type for this {@link Control}, the old value must be
- * passed.
- * @param controlTemplate the template to be used to render the {@link Control}. This can be
- * of a different {@link ControlTemplate.TemplateType} than the
- * one defined in {@link Control#getPrimaryType}
- * @param statusText the user facing text describing the current status.
- * @param appIntent the {@link PendingIntent} to replace the one set in the {@link Control} or
- * set in the last {@link ControlState}. Pass {@code null} to use the last
- * value set for this {@link Control}.
- */
- public ControlState(@NonNull String controlId,
- @DeviceTypes.DeviceType int deviceType,
- @Status int status,
- @NonNull ControlTemplate controlTemplate,
- @NonNull CharSequence statusText,
- @Nullable PendingIntent appIntent) {
- Preconditions.checkNotNull(controlId);
- Preconditions.checkNotNull(controlTemplate);
- Preconditions.checkNotNull(statusText);
- mControlId = controlId;
- if (!DeviceTypes.validDeviceType(deviceType)) {
- Log.e(TAG, "Invalid device type:" + deviceType);
- mDeviceType = DeviceTypes.TYPE_UNKNOWN;
- } else {
- mDeviceType = deviceType;
- }
- mStatus = status;
- mControlTemplate = controlTemplate;
- mStatusText = statusText;
- mAppIntent = appIntent;
- }
-
- ControlState(Parcel in) {
- mControlId = in.readString();
- mDeviceType = in.readInt();
- mStatus = in.readInt();
- mControlTemplate = ControlTemplate.CREATOR.createFromParcel(in);
- mStatusText = in.readCharSequence();
- if (in.readByte() == 1) {
- mAppIntent = PendingIntent.CREATOR.createFromParcel(in);
- } else {
- mAppIntent = null;
- }
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @NonNull
- public String getControlId() {
- return mControlId;
- }
-
- @DeviceTypes.DeviceType
- public int getDeviceType() {
- return mDeviceType;
- }
-
- @Nullable
- public PendingIntent getAppIntent() {
- return mAppIntent;
- }
-
- @Status
- public int getStatus() {
- return mStatus;
- }
-
- @NonNull
- public ControlTemplate getControlTemplate() {
- return mControlTemplate;
- }
-
- @NonNull
- public CharSequence getStatusText() {
- return mStatusText;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeString(mControlId);
- dest.writeInt(mDeviceType);
- dest.writeInt(mStatus);
- mControlTemplate.writeToParcel(dest, flags);
- dest.writeCharSequence(mStatusText);
- if (mAppIntent != null) {
- dest.writeByte((byte) 1);
- mAppIntent.writeToParcel(dest, flags);
- } else {
- dest.writeByte((byte) 0);
- }
- }
-
- public static final Creator<ControlState> CREATOR = new Creator<ControlState>() {
- @Override
- public ControlState createFromParcel(Parcel source) {
- return new ControlState(source);
- }
-
- @Override
- public ControlState[] newArray(int size) {
- return new ControlState[size];
- }
- };
-
- /**
- * Builder class for {@link ControlState}.
- *
- * This class facilitates the creation of {@link ControlState}. It provides the following
- * defaults for non-optional parameters:
- * <ul>
- * <li> Device type: {@link DeviceTypes#TYPE_UNKNOWN}
- * <li> Status: {@link ControlState#STATUS_OK}
- * <li> Control template: {@link ControlTemplate#NO_TEMPLATE}
- * <li> Status text: {@code ""}
- * </ul>
- */
- public static class Builder {
- private static final String TAG = "ControlState.Builder";
-
- private @NonNull String mControlId;
- private @DeviceTypes.DeviceType
- int mDeviceType = DeviceTypes.TYPE_UNKNOWN;
- private @Status int mStatus = STATUS_OK;
- private @NonNull ControlTemplate mControlTemplate = ControlTemplate.NO_TEMPLATE;
- private @NonNull CharSequence mStatusText = "";
- private @Nullable PendingIntent mAppIntent;
-
- /**
- * @param controlId the identifier of the {@link Control} that the resulting
- * {@link ControlState} refers to.
- */
- public Builder(@NonNull String controlId) {
- Preconditions.checkNotNull(controlId);
- mControlId = controlId;
- }
-
- /**
- * Creates a {@link Builder} using an existing {@link ControlState} as a base.
- * @param controlState base for the builder.
- */
- public Builder(@NonNull ControlState controlState) {
- Preconditions.checkNotNull(controlState);
- mControlId = controlState.mControlId;
- mDeviceType = controlState.mDeviceType;
- mStatus = controlState.mStatus;
- mControlTemplate = controlState.mControlTemplate;
- mStatusText = controlState.mStatusText;
- mAppIntent = controlState.mAppIntent;
- }
-
-
- /**
- * @param controlId the identifier of the {@link Control} for the resulting object.
- * @return {@code this}
- */
- @NonNull
- public Builder setControlId(@NonNull String controlId) {
- mControlId = controlId;
- return this;
- }
-
- /**
- * @param deviceType the device type of the {@link Control}.
- * @return {@code this}
- */
- @NonNull
- public Builder setDeviceType(@DeviceTypes.DeviceType int deviceType) {
- if (!DeviceTypes.validDeviceType(deviceType)) {
- Log.e(TAG, "Invalid device type:" + deviceType);
- mDeviceType = DeviceTypes.TYPE_UNKNOWN;
- } else {
- mDeviceType = deviceType;
- }
- return this;
- }
-
- /**
- * @param status the current status of the {@link Control}
- * @return {@code this}
- */
- @NonNull
- public Builder setStatus(@Status int status) {
- mStatus = status;
- return this;
- }
-
- /**
- * @param controlTemplate the template to use when rendering the {@code Control}.
- * @return {@code this}
- */
- @NonNull
- public Builder setControlTemplate(@NonNull ControlTemplate controlTemplate) {
- Preconditions.checkNotNull(controlTemplate);
- mControlTemplate = controlTemplate;
- return this;
- }
-
- /**
- * @param statusText the user-visible description of the status.
- * @return {@code this}
- */
- @NonNull
- public Builder setStatusText(@NonNull CharSequence statusText) {
- Preconditions.checkNotNull(statusText);
- mStatusText = statusText;
- return this;
- }
-
- /**
- * @param appIntent the Pending Intent to replace the one defined in the corresponding
- * {@link Control} or set by the last {@link ControlState}. Pass
- * {@code null} to keep the last value.
- * @return {@code this}
- */
- @NonNull
- public Builder setAppIntent(@Nullable PendingIntent appIntent) {
- mAppIntent = appIntent;
- return this;
- }
-
- /**
- * @return a new {@link ControlState}
- */
- public ControlState build() {
- return new ControlState(mControlId, mDeviceType, mStatus, mControlTemplate, mStatusText,
- mAppIntent);
- }
-
- /**
- * Creates a new {@link ControlState.Builder} for the given {@link Control}.
- *
- * This will set the corresponding identifier as well as the device type.
- * @param control the {@link Control} to create a state for.
- * @return a {@link ControlState.Builder} for a {@link Control}
- */
- public static Builder createForControl(Control control) {
- return new Builder(control.getControlId()).setDeviceType(control.getDeviceType());
- }
- }
-}
-
diff --git a/core/java/android/service/controls/ControlsProviderService.java b/core/java/android/service/controls/ControlsProviderService.java
index 8a95f4d..2fd51a1 100644
--- a/core/java/android/service/controls/ControlsProviderService.java
+++ b/core/java/android/service/controls/ControlsProviderService.java
@@ -20,13 +20,21 @@
import android.annotation.SdkConstant.SdkConstantType;
import android.app.Service;
import android.content.Intent;
+import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.RemoteException;
import android.service.controls.actions.ControlAction;
+import android.service.controls.templates.ControlTemplate;
+import android.text.TextUtils;
+import android.util.Log;
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
import java.util.List;
/**
@@ -38,8 +46,14 @@
@SdkConstant(SdkConstantType.SERVICE_ACTION)
public static final String CONTROLS_ACTION = "android.service.controls.ControlsProviderService";
+ public static final String CALLBACK_BUNDLE = "CALLBACK_BUNDLE";
+ public static final String CALLBACK_BINDER = "CALLBACK_BINDER";
+ public static final String CALLBACK_TOKEN = "CALLBACK_TOKEN";
+
+ public final String TAG = getClass().getSimpleName();
private IControlsProviderCallback mCallback;
+ private IBinder mToken;
private RequestHandler mHandler;
/**
@@ -67,16 +81,80 @@
*/
public abstract void onAction(@NonNull String controlId, @NonNull ControlAction action);
- protected IControlsProviderCallback getControlsProviderCallback() {
- return mCallback;
+ /**
+ * Sends a list of the controls available from this service.
+ *
+ * The items in the list must not have state information (as created by
+ * {@link Control.StatelessBuilder}).
+ * @param controls
+ */
+ public void onLoad(@NonNull List<Control> controls) {
+ Preconditions.checkNotNull(controls);
+ List<Control> list = new ArrayList<>();
+ for (Control control: controls) {
+ if (control == null) {
+ Log.e(TAG, "onLoad: null control.");
+ }
+ if (isStateless(control)) {
+ list.add(control);
+ } else {
+ Log.w(TAG, "onLoad: control is not stateless.");
+ list.add(new Control.StatelessBuilder(control).build());
+ }
+ }
+ try {
+ mCallback.onLoad(mToken, list);
+ } catch (RemoteException ex) {
+ ex.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Sends a list of the controls requested by {@link ControlsProviderService#subscribe} with
+ * their state.
+ * @param statefulControls
+ */
+ public void onRefreshState(@NonNull List<Control> statefulControls) {
+ Preconditions.checkNotNull(statefulControls);
+ try {
+ mCallback.onRefreshState(mToken, statefulControls);
+ } catch (RemoteException ex) {
+ ex.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Sends the response of a command in the specified {@link Control}.
+ * @param controlId
+ * @param response
+ */
+ public void onControlActionResponse(
+ @NonNull String controlId, @ControlAction.ResponseResult int response) {
+ Preconditions.checkNotNull(controlId);
+ if (!ControlAction.isValidResponse(response)) {
+ Log.e(TAG, "Not valid response result: " + response);
+ response = ControlAction.RESPONSE_UNKNOWN;
+ }
+ try {
+ mCallback.onControlActionResponse(mToken, controlId, response);
+ } catch (RemoteException ex) {
+ ex.rethrowAsRuntimeException();
+ }
+ }
+
+ private boolean isStateless(Control control) {
+ return (control.getStatus() == Control.STATUS_UNKNOWN
+ && control.getControlTemplate().getTemplateType() == ControlTemplate.TYPE_NONE
+ && TextUtils.isEmpty(control.getStatusText()));
}
@Override
public IBinder onBind(Intent intent) {
mHandler = new RequestHandler(Looper.getMainLooper());
- Bundle bundle = intent.getBundleExtra("CALLBACK_BUNDLE");
- IBinder callbackBinder = bundle.getBinder("CALLBACK_BINDER");
+ Bundle bundle = intent.getBundleExtra(CALLBACK_BUNDLE);
+ IBinder callbackBinder = bundle.getBinder(CALLBACK_BINDER);
+ mToken = bundle.getBinder(CALLBACK_TOKEN);
mCallback = IControlsProviderCallback.Stub.asInterface(callbackBinder);
return new IControlsProvider.Stub() {
diff --git a/core/java/android/service/controls/IControlsProviderCallback.aidl b/core/java/android/service/controls/IControlsProviderCallback.aidl
index 3dbb68c..91f6a79 100644
--- a/core/java/android/service/controls/IControlsProviderCallback.aidl
+++ b/core/java/android/service/controls/IControlsProviderCallback.aidl
@@ -17,13 +17,12 @@
package android.service.controls;
import android.service.controls.Control;
-import android.service.controls.ControlState;
/** @hide */
oneway interface IControlsProviderCallback {
- void onLoad(in List<Control> controls);
+ void onLoad(in IBinder token, in List<Control> controls);
- void onRefreshState(in List<ControlState> controlStates);
+ void onRefreshState(in IBinder token, in List<Control> statefulControls);
- void onControlActionResponse(in String controlId, int response);
+ void onControlActionResponse(in IBinder token, in String controlId, int response);
}
\ No newline at end of file
diff --git a/core/java/android/service/controls/actions/ControlAction.java b/core/java/android/service/controls/actions/ControlAction.java
index 63ae9bd..83d1cf8 100644
--- a/core/java/android/service/controls/actions/ControlAction.java
+++ b/core/java/android/service/controls/actions/ControlAction.java
@@ -23,8 +23,8 @@
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
-import android.service.controls.templates.ControlTemplate;
import android.service.controls.IControlsProviderCallback;
+import android.service.controls.templates.ControlTemplate;
import com.android.internal.util.Preconditions;
@@ -82,11 +82,17 @@
public static final @ActionType int TYPE_COMMAND = 5;
+
+ public static final boolean isValidResponse(@ResponseResult int response) {
+ return (response >= 0 && response < NUM_RESPONSE_TYPES);
+ }
+ private static final int NUM_RESPONSE_TYPES = 6;
/**
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef({
+ RESPONSE_UNKNOWN,
RESPONSE_OK,
RESPONSE_FAIL,
RESPONSE_CHALLENGE_ACK,
@@ -95,31 +101,33 @@
})
public @interface ResponseResult {};
+ public static final @ResponseResult int RESPONSE_UNKNOWN = 0;
+
/**
* Response code for {@link IControlsProviderCallback#onControlActionResponse} indicating that
* the action has been performed. The action may still fail later and the state may not change.
*/
- public static final @ResponseResult int RESPONSE_OK = 0;
+ public static final @ResponseResult int RESPONSE_OK = 1;
/**
* Response code for {@link IControlsProviderCallback#onControlActionResponse} indicating that
* the action has failed.
*/
- public static final @ResponseResult int RESPONSE_FAIL = 1;
+ public static final @ResponseResult int RESPONSE_FAIL = 2;
/**
* Response code for {@link IControlsProviderCallback#onControlActionResponse} indicating that
* in order for the action to be performed, acknowledgment from the user is required.
*/
- public static final @ResponseResult int RESPONSE_CHALLENGE_ACK = 2;
+ public static final @ResponseResult int RESPONSE_CHALLENGE_ACK = 3;
/**
* Response code for {@link IControlsProviderCallback#onControlActionResponse} indicating that
* in order for the action to be performed, a PIN is required.
*/
- public static final @ResponseResult int RESPONSE_CHALLENGE_PIN = 3;
+ public static final @ResponseResult int RESPONSE_CHALLENGE_PIN = 4;
/**
* Response code for {@link IControlsProviderCallback#onControlActionResponse} indicating that
* in order for the action to be performed, an alphanumeric passphrase is required.
*/
- public static final @ResponseResult int RESPONSE_CHALLENGE_PASSPHRASE = 4;
+ public static final @ResponseResult int RESPONSE_CHALLENGE_PASSPHRASE = 5;
/**
* The {@link ActionType} associated with this class.
diff --git a/core/java/android/service/controls/templates/ControlTemplate.java b/core/java/android/service/controls/templates/ControlTemplate.java
index f39b26e..bf194f8 100644
--- a/core/java/android/service/controls/templates/ControlTemplate.java
+++ b/core/java/android/service/controls/templates/ControlTemplate.java
@@ -23,7 +23,6 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.service.controls.Control;
-import android.service.controls.ControlState;
import android.service.controls.actions.ControlAction;
import com.android.internal.util.Preconditions;
@@ -34,12 +33,11 @@
/**
* An abstract input template for a {@link Control}.
*
- * Specifies what layout is presented to the user when a {@link ControlState} is assigned to a
- * particular {@link Control}.
+ * Specifies what layout is presented to the user for a given {@link Control}.
* <p>
* Some instances of {@link Control} can originate actions (via user interaction) to modify its
- * associated state. The actions available to a given {@link Control} in a particular
- * {@link ControlState} are determined by its {@link ControlTemplate}.
+ * associated state. The actions available to a given {@link Control} are determined by its
+ * {@link ControlTemplate}.
* @see ControlAction
* @hide
*/
diff --git a/core/java/android/service/controls/templates/RangeTemplate.java b/core/java/android/service/controls/templates/RangeTemplate.java
index 5624f88..bb79d83 100644
--- a/core/java/android/service/controls/templates/RangeTemplate.java
+++ b/core/java/android/service/controls/templates/RangeTemplate.java
@@ -21,7 +21,6 @@
import android.os.Bundle;
import android.os.Parcel;
import android.service.controls.Control;
-import android.service.controls.ControlState;
import android.service.controls.actions.FloatAction;
/**
@@ -61,7 +60,7 @@
* @param templateId the identifier for this template object
* @param minValue minimum value for the input
* @param maxValue maximum value for the input
- * @param currentValue the current value of the {@link ControlState} containing this object.
+ * @param currentValue the current value of the {@link Control} containing this object.
* @param stepValue minimum value of increments/decrements when interacting with this control.
* @param formatString a formatting string as per {@link String#format} used to display the
* {@code currentValue}. If {@code null} is passed, the "%.1f" is used.
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 6350bf6..85f7d61 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1756,6 +1756,12 @@
The default value is {@code false}. -->
<attr name="forceQueryable" format="boolean" />
+
+ <!-- If {@code true} indicates that this application is capable of presenting a unified
+ interface representing multiple profiles.
+
+ The default value is {@code false}. -->
+ <attr name="crossProfile" format="boolean" />
</declare-styleable>
<!-- The <code>permission</code> tag declares a security permission that can be
used to control access from other packages to specific components or
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
index 732e8db..99c7dcf 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
@@ -17,9 +17,8 @@
import android.content.Context;
import android.graphics.drawable.Drawable;
-import android.widget.Toast;
-
-import androidx.mediarouter.media.MediaRouter;
+import android.media.MediaRoute2Info;
+import android.media.MediaRouter2Manager;
import com.android.settingslib.R;
import com.android.settingslib.bluetooth.BluetoothUtils;
@@ -31,22 +30,28 @@
private static final String TAG = "InfoMediaDevice";
- private MediaRouter.RouteInfo mRouteInfo;
+ private final MediaRoute2Info mRouteInfo;
+ private final MediaRouter2Manager mRouterManager;
+ private final String mPackageName;
- InfoMediaDevice(Context context, MediaRouter.RouteInfo info) {
+ InfoMediaDevice(Context context, MediaRouter2Manager routerManager, MediaRoute2Info info,
+ String packageName) {
super(context, MediaDeviceType.TYPE_CAST_DEVICE);
+ mRouterManager = routerManager;
mRouteInfo = info;
+ mPackageName = packageName;
initDeviceRecord();
}
@Override
public String getName() {
- return mRouteInfo.getName();
+ return mRouteInfo.getName().toString();
}
@Override
public String getSummary() {
- return null;
+ return mRouteInfo.getClientPackageName() != null
+ ? mContext.getString(R.string.bluetooth_active_no_battery_level) : null;
}
@Override
@@ -63,15 +68,14 @@
@Override
public boolean connect() {
- //TODO(b/121083246): use SystemApi to transfer media
setConnectedRecord();
- Toast.makeText(mContext, "This is cast device !", Toast.LENGTH_SHORT).show();
- return false;
+ mRouterManager.selectRoute(mPackageName, mRouteInfo);
+ return true;
}
@Override
public void disconnect() {
- //TODO(b/121083246): disconnected last select device
+ //TODO(b/144535188): disconnected last select device
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index bc8e2c3..e008cd03 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -17,13 +17,16 @@
import android.app.Notification;
import android.content.Context;
-import android.util.Log;
-
-import androidx.mediarouter.media.MediaRouteSelector;
-import androidx.mediarouter.media.MediaRouter;
+import android.media.MediaRoute2Info;
+import android.media.MediaRouter2Manager;
+import android.text.TextUtils;
import com.android.internal.annotations.VisibleForTesting;
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
/**
* InfoMediaManager provide interface to get InfoMediaDevice list.
*/
@@ -32,62 +35,75 @@
private static final String TAG = "InfoMediaManager";
@VisibleForTesting
- final MediaRouterCallback mMediaRouterCallback = new MediaRouterCallback();
+ final RouterManagerCallback mMediaRouterCallback = new RouterManagerCallback();
@VisibleForTesting
- MediaRouteSelector mSelector;
+ final Executor mExecutor = Executors.newSingleThreadExecutor();
@VisibleForTesting
- MediaRouter mMediaRouter;
+ MediaRouter2Manager mRouterManager;
private String mPackageName;
+ private MediaDevice mCurrentConnectedDevice;
- InfoMediaManager(Context context, String packageName, Notification notification) {
+ public InfoMediaManager(Context context, String packageName, Notification notification) {
super(context, notification);
- mMediaRouter = MediaRouter.getInstance(context);
- mPackageName = packageName;
- mSelector = new MediaRouteSelector.Builder()
- .addControlCategory(getControlCategoryByPackageName(mPackageName))
- .build();
+ mRouterManager = MediaRouter2Manager.getInstance(context);
+ if (packageName != null) {
+ mPackageName = packageName;
+ }
}
@Override
public void startScan() {
mMediaDevices.clear();
- mMediaRouter.addCallback(mSelector, mMediaRouterCallback,
- MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY);
+ mRouterManager.registerCallback(mExecutor, mMediaRouterCallback);
}
@VisibleForTesting
String getControlCategoryByPackageName(String packageName) {
//TODO(b/117129183): Use package name to get ControlCategory.
//Since api not ready, return fixed ControlCategory for prototype.
- return "com.google.android.gms.cast.CATEGORY_CAST/4F8B3483";
+ return "com.google.android.gms.cast.CATEGORY_CAST";
}
@Override
public void stopScan() {
- mMediaRouter.removeCallback(mMediaRouterCallback);
+ mRouterManager.unregisterCallback(mMediaRouterCallback);
}
- class MediaRouterCallback extends MediaRouter.Callback {
- @Override
- public void onRouteAdded(MediaRouter router, MediaRouter.RouteInfo route) {
- MediaDevice mediaDevice = findMediaDevice(MediaDeviceUtils.getId(route));
- if (mediaDevice == null) {
- mediaDevice = new InfoMediaDevice(mContext, route);
- Log.d(TAG, "onRouteAdded() route : " + route.getName());
- mMediaDevices.add(mediaDevice);
- dispatchDeviceAdded(mediaDevice);
+ /**
+ * Get current device that played media.
+ * @return MediaDevice
+ */
+ public MediaDevice getCurrentConnectedDevice() {
+ return mCurrentConnectedDevice;
+ }
+
+ class RouterManagerCallback extends MediaRouter2Manager.Callback {
+
+ private void refreshDevices() {
+ mMediaDevices.clear();
+ mCurrentConnectedDevice = null;
+ for (MediaRoute2Info route : mRouterManager.getAvailableRoutes(mPackageName)) {
+ final MediaDevice device = new InfoMediaDevice(mContext, mRouterManager, route,
+ mPackageName);
+ if (TextUtils.equals(route.getClientPackageName(), mPackageName)) {
+ mCurrentConnectedDevice = device;
+ }
+ mMediaDevices.add(device);
}
+ dispatchDeviceListAdded();
}
@Override
- public void onRouteRemoved(MediaRouter router, MediaRouter.RouteInfo route) {
- final MediaDevice mediaDevice = findMediaDevice(MediaDeviceUtils.getId(route));
- if (mediaDevice != null) {
- Log.d(TAG, "onRouteRemoved() route : " + route.getName());
- mMediaDevices.remove(mediaDevice);
- dispatchDeviceRemoved(mediaDevice);
+ public void onRoutesAdded(List<MediaRoute2Info> routes) {
+ refreshDevices();
+ }
+
+ @Override
+ public void onControlCategoriesChanged(String packageName, List<String> controlCategories) {
+ if (TextUtils.equals(mPackageName, packageName)) {
+ refreshDevices();
}
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index 4e16c66..5b4ef3a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -18,6 +18,7 @@
import android.app.Notification;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
+import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.IntDef;
@@ -59,6 +60,8 @@
private Context mContext;
private BluetoothMediaManager mBluetoothMediaManager;
private LocalBluetoothManager mLocalBluetoothManager;
+ private InfoMediaManager mInfoMediaManager;
+ private String mPackageName;
@VisibleForTesting
List<MediaDevice> mMediaDevices = new ArrayList<>();
@@ -87,6 +90,7 @@
public LocalMediaManager(Context context, String packageName, Notification notification) {
mContext = context;
+ mPackageName = packageName;
mLocalBluetoothManager =
LocalBluetoothManager.getInstance(context, /* onInitCallback= */ null);
if (mLocalBluetoothManager == null) {
@@ -96,6 +100,7 @@
mBluetoothMediaManager =
new BluetoothMediaManager(context, mLocalBluetoothManager, notification);
+ mInfoMediaManager = new InfoMediaManager(context, packageName, notification);
}
@VisibleForTesting
@@ -104,6 +109,7 @@
mContext = context;
mLocalBluetoothManager = localBluetoothManager;
mBluetoothMediaManager = bluetoothMediaManager;
+ mInfoMediaManager = infoMediaManager;
}
/**
@@ -126,8 +132,7 @@
return;
}
- //TODO(b/121083246): Update it once remote media API is ready.
- if (mCurrentConnectedDevice != null && !(connectDevice instanceof InfoMediaDevice)) {
+ if (mCurrentConnectedDevice != null) {
mCurrentConnectedDevice.disconnect();
}
@@ -157,6 +162,10 @@
mMediaDevices.clear();
mBluetoothMediaManager.registerCallback(mMediaDeviceCallback);
mBluetoothMediaManager.startScan();
+ if (!TextUtils.isEmpty(mPackageName)) {
+ mInfoMediaManager.registerCallback(mMediaDeviceCallback);
+ mInfoMediaManager.startScan();
+ }
}
private void addPhoneDeviceIfNecessary() {
@@ -191,6 +200,10 @@
public void stopScan() {
mBluetoothMediaManager.unregisterCallback(mMediaDeviceCallback);
mBluetoothMediaManager.stopScan();
+ if (!TextUtils.isEmpty(mPackageName)) {
+ mInfoMediaManager.unregisterCallback(mMediaDeviceCallback);
+ mInfoMediaManager.stopScan();
+ }
}
/**
@@ -253,7 +266,9 @@
}
}
addPhoneDeviceIfNecessary();
- mCurrentConnectedDevice = updateCurrentConnectedDevice();
+ final MediaDevice infoMediaDevice = mInfoMediaManager.getCurrentConnectedDevice();
+ mCurrentConnectedDevice = infoMediaDevice != null
+ ? infoMediaDevice : updateCurrentConnectedDevice();
updatePhoneMediaDeviceSummary();
dispatchDeviceListUpdate();
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDeviceUtils.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDeviceUtils.java
index 4b8e706..df6929e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDeviceUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDeviceUtils.java
@@ -16,8 +16,7 @@
package com.android.settingslib.media;
import android.bluetooth.BluetoothDevice;
-
-import androidx.mediarouter.media.MediaRouter;
+import android.media.MediaRoute2Info;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
@@ -49,12 +48,12 @@
}
/**
- * Use RouteInfo id to represent unique id
+ * Use MediaRoute2Info id to represent unique id
*
- * @param route the RouteInfo
- * @return RouteInfo id
+ * @param route the MediaRoute2Info
+ * @return MediaRoute2Info id
*/
- public static String getId(MediaRouter.RouteInfo route) {
+ public static String getId(MediaRoute2Info route) {
return route.getId();
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java
new file mode 100644
index 0000000..c9db0d1
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java
@@ -0,0 +1,98 @@
+/*
+ * 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.
+ */
+
+package com.android.settingslib.media;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.media.MediaRoute2Info;
+import android.media.MediaRouter2Manager;
+
+import com.android.settingslib.R;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class InfoMediaDeviceTest {
+
+ private static final String TEST_PACKAGE_NAME = "com.test.packagename";
+ private static final String TEST_ID = "test_id";
+ private static final String TEST_NAME = "test_name";
+
+ @Mock
+ private MediaRouter2Manager mRouterManager;
+ @Mock
+ private MediaRoute2Info mRouteInfo;
+
+
+ private Context mContext;
+ private InfoMediaDevice mInfoMediaDevice;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = RuntimeEnvironment.application;
+
+ mInfoMediaDevice = new InfoMediaDevice(mContext, mRouterManager, mRouteInfo,
+ TEST_PACKAGE_NAME);
+ }
+
+ @Test
+ public void getName_shouldReturnName() {
+ when(mRouteInfo.getName()).thenReturn(TEST_NAME);
+
+ assertThat(mInfoMediaDevice.getName()).isEqualTo(TEST_NAME);
+ }
+
+ @Test
+ public void getSummary_clientPackageNameIsNull_returnNull() {
+ when(mRouteInfo.getClientPackageName()).thenReturn(null);
+
+ assertThat(mInfoMediaDevice.getSummary()).isEqualTo(null);
+ }
+
+ @Test
+ public void getSummary_clientPackageNameIsNotNull_returnActive() {
+ when(mRouteInfo.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+
+ assertThat(mInfoMediaDevice.getSummary())
+ .isEqualTo(mContext.getString(R.string.bluetooth_active_no_battery_level));
+ }
+
+ @Test
+ public void getId_shouldReturnId() {
+ when(mRouteInfo.getId()).thenReturn(TEST_ID);
+
+ assertThat(mInfoMediaDevice.getId()).isEqualTo(TEST_ID);
+ }
+
+ @Test
+ public void connect_shouldSelectRoute() {
+ mInfoMediaDevice.connect();
+
+ verify(mRouterManager).selectRoute(TEST_PACKAGE_NAME, mRouteInfo);
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
index b11cf69..67f6dd90 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
@@ -23,9 +23,8 @@
import static org.mockito.Mockito.when;
import android.content.Context;
-
-import androidx.mediarouter.media.MediaRouteSelector;
-import androidx.mediarouter.media.MediaRouter;
+import android.media.MediaRoute2Info;
+import android.media.MediaRouter2Manager;
import org.junit.Before;
import org.junit.Test;
@@ -35,6 +34,9 @@
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import java.util.ArrayList;
+import java.util.List;
+
@RunWith(RobolectricTestRunner.class)
public class InfoMediaManagerTest {
@@ -42,9 +44,7 @@
private static final String TEST_ID = "test_id";
@Mock
- private MediaRouter mMediaRouter;
- @Mock
- private MediaRouteSelector mSelector;
+ private MediaRouter2Manager mRouterManager;
private InfoMediaManager mInfoMediaManager;
private Context mContext;
@@ -55,82 +55,70 @@
mContext = RuntimeEnvironment.application;
mInfoMediaManager = new InfoMediaManager(mContext, TEST_PACKAGE_NAME, null);
- mInfoMediaManager.mMediaRouter = mMediaRouter;
- mInfoMediaManager.mSelector = mSelector;
+ mInfoMediaManager.mRouterManager = mRouterManager;
}
@Test
public void stopScan_shouldRemoveCallback() {
mInfoMediaManager.stopScan();
- verify(mMediaRouter).removeCallback(mInfoMediaManager.mMediaRouterCallback);
+ verify(mRouterManager).unregisterCallback(mInfoMediaManager.mMediaRouterCallback);
}
@Test
public void startScan_shouldAddCallback() {
mInfoMediaManager.startScan();
- verify(mMediaRouter).addCallback(mSelector, mInfoMediaManager.mMediaRouterCallback,
- MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY);
+ verify(mRouterManager).registerCallback(mInfoMediaManager.mExecutor,
+ mInfoMediaManager.mMediaRouterCallback);
}
@Test
- public void onRouteAdded_mediaDeviceNotExistInList_addMediaDevice() {
- final MediaRouter.RouteInfo info = mock(MediaRouter.RouteInfo.class);
+ public void onRouteAdded_shouldAddMediaDevice() {
+ final MediaRoute2Info info = mock(MediaRoute2Info.class);
when(info.getId()).thenReturn(TEST_ID);
+ when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+
+ final List<MediaRoute2Info> routes = new ArrayList<>();
+ routes.add(info);
+ when(mRouterManager.getAvailableRoutes(TEST_PACKAGE_NAME)).thenReturn(routes);
final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID);
assertThat(mediaDevice).isNull();
- mInfoMediaManager.mMediaRouterCallback.onRouteAdded(mMediaRouter, info);
+ mInfoMediaManager.mMediaRouterCallback.onRoutesAdded(routes);
final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0);
assertThat(infoDevice.getId()).isEqualTo(TEST_ID);
+ assertThat(mInfoMediaManager.getCurrentConnectedDevice()).isEqualTo(infoDevice);
+ assertThat(mInfoMediaManager.mMediaDevices).hasSize(routes.size());
}
@Test
- public void onRouteAdded_mediaDeviceExistInList_doNothing() {
- final MediaRouter.RouteInfo info = mock(MediaRouter.RouteInfo.class);
+ public void onControlCategoriesChanged_samePackageName_shouldAddMediaDevice() {
+ final MediaRoute2Info info = mock(MediaRoute2Info.class);
when(info.getId()).thenReturn(TEST_ID);
- final InfoMediaDevice infoDevice = new InfoMediaDevice(mContext, info);
- mInfoMediaManager.mMediaDevices.add(infoDevice);
+ when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+
+ final List<MediaRoute2Info> routes = new ArrayList<>();
+ routes.add(info);
+ when(mRouterManager.getAvailableRoutes(TEST_PACKAGE_NAME)).thenReturn(routes);
final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID);
- final int size = mInfoMediaManager.mMediaDevices.size();
- assertThat(mediaDevice).isNotNull();
-
- mInfoMediaManager.mMediaRouterCallback.onRouteAdded(mMediaRouter, info);
-
- assertThat(mInfoMediaManager.mMediaDevices).hasSize(size);
- }
-
- @Test
- public void onRouteRemoved_mediaDeviceExistInList_removeMediaDevice() {
- final MediaRouter.RouteInfo info = mock(MediaRouter.RouteInfo.class);
- when(info.getId()).thenReturn(TEST_ID);
- final InfoMediaDevice infoDevice = new InfoMediaDevice(mContext, info);
- mInfoMediaManager.mMediaDevices.add(infoDevice);
-
- final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID);
- assertThat(mediaDevice).isNotNull();
- assertThat(mInfoMediaManager.mMediaDevices).hasSize(1);
-
- mInfoMediaManager.mMediaRouterCallback.onRouteRemoved(mMediaRouter, info);
-
- assertThat(mInfoMediaManager.mMediaDevices).isEmpty();
- }
-
- @Test
- public void onRouteRemoved_mediaDeviceNotExistInList_doNothing() {
- final MediaRouter.RouteInfo info = mock(MediaRouter.RouteInfo.class);
- when(info.getId()).thenReturn(TEST_ID);
-
- final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID);
- final int size = mInfoMediaManager.mMediaDevices.size();
assertThat(mediaDevice).isNull();
- mInfoMediaManager.mMediaRouterCallback.onRouteRemoved(mMediaRouter, info);
+ mInfoMediaManager.mMediaRouterCallback.onControlCategoriesChanged(TEST_PACKAGE_NAME, null);
- assertThat(mInfoMediaManager.mMediaDevices).hasSize(size);
+ final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0);
+ assertThat(infoDevice.getId()).isEqualTo(TEST_ID);
+ assertThat(mInfoMediaManager.getCurrentConnectedDevice()).isEqualTo(infoDevice);
+ assertThat(mInfoMediaManager.mMediaDevices).hasSize(routes.size());
+ }
+
+ @Test
+ public void onControlCategoriesChanged_differentPackageName_doNothing() {
+ mInfoMediaManager.mMediaRouterCallback.onControlCategoriesChanged("com.fake.play", null);
+
+ assertThat(mInfoMediaManager.mMediaDevices).hasSize(0);
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
index 23d2c74..02cb83e 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
@@ -22,8 +22,8 @@
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
-
-import androidx.mediarouter.media.MediaRouter;
+import android.media.MediaRoute2Info;
+import android.media.MediaRouter2Manager;
import com.android.settingslib.bluetooth.A2dpProfile;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
@@ -56,6 +56,7 @@
private static final String ROUTER_ID_1 = "RouterId_1";
private static final String ROUTER_ID_2 = "RouterId_2";
private static final String ROUTER_ID_3 = "RouterId_3";
+ private static final String TEST_PACKAGE_NAME = "com.test.playmusic";
private final BluetoothClass mHeadreeClass =
new BluetoothClass(BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES);
private final BluetoothClass mCarkitClass =
@@ -76,11 +77,11 @@
@Mock
private LocalBluetoothManager mLocalBluetoothManager;
@Mock
- private MediaRouter.RouteInfo mRouteInfo1;
+ private MediaRoute2Info mRouteInfo1;
@Mock
- private MediaRouter.RouteInfo mRouteInfo2;
+ private MediaRoute2Info mRouteInfo2;
@Mock
- private MediaRouter.RouteInfo mRouteInfo3;
+ private MediaRoute2Info mRouteInfo3;
@Mock
private LocalBluetoothProfileManager mProfileManager;
@Mock
@@ -99,6 +100,7 @@
private InfoMediaDevice mInfoMediaDevice3;
private List<MediaDevice> mMediaDevices = new ArrayList<>();
private PhoneMediaDevice mPhoneMediaDevice;
+ private MediaRouter2Manager mMediaRouter2Manager;
@Before
public void setUp() {
@@ -134,9 +136,13 @@
mBluetoothMediaDevice1 = new BluetoothMediaDevice(mContext, mCachedDevice1);
mBluetoothMediaDevice2 = new BluetoothMediaDevice(mContext, mCachedDevice2);
mBluetoothMediaDevice3 = new BluetoothMediaDevice(mContext, mCachedDevice3);
- mInfoMediaDevice1 = new InfoMediaDevice(mContext, mRouteInfo1);
- mInfoMediaDevice2 = new InfoMediaDevice(mContext, mRouteInfo2);
- mInfoMediaDevice3 = new InfoMediaDevice(mContext, mRouteInfo3);
+ mMediaRouter2Manager = MediaRouter2Manager.getInstance(mContext);
+ mInfoMediaDevice1 = new InfoMediaDevice(mContext, mMediaRouter2Manager, mRouteInfo1,
+ TEST_PACKAGE_NAME);
+ mInfoMediaDevice2 = new InfoMediaDevice(mContext, mMediaRouter2Manager, mRouteInfo2,
+ TEST_PACKAGE_NAME);
+ mInfoMediaDevice3 = new InfoMediaDevice(mContext, mMediaRouter2Manager, mRouteInfo3,
+ TEST_PACKAGE_NAME);
mPhoneMediaDevice = new PhoneMediaDevice(mContext, mLocalBluetoothManager);
}
@@ -364,5 +370,4 @@
assertThat(mMediaDevices.get(5)).isEqualTo(mBluetoothMediaDevice1);
assertThat(mMediaDevices.get(6)).isEqualTo(mBluetoothMediaDevice2);
}
-
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceUtilsTest.java
index 1e5545f..30a6ad2 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceUtilsTest.java
@@ -21,8 +21,7 @@
import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothDevice;
-
-import androidx.mediarouter.media.MediaRouter;
+import android.media.MediaRoute2Info;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
@@ -44,7 +43,7 @@
@Mock
private BluetoothDevice mBluetoothDevice;
@Mock
- private MediaRouter.RouteInfo mRouteInfo;
+ private MediaRoute2Info mRouteInfo;
@Before
public void setUp() {
diff --git a/services/print/java/com/android/server/print/PrintManagerService.java b/services/print/java/com/android/server/print/PrintManagerService.java
index c9b9f3e..d064f7e 100644
--- a/services/print/java/com/android/server/print/PrintManagerService.java
+++ b/services/print/java/com/android/server/print/PrintManagerService.java
@@ -78,6 +78,7 @@
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
+import java.util.Objects;
/**
* SystemService wrapper for the PrintManager implementation. Publishes
@@ -137,7 +138,7 @@
@Override
public Bundle print(String printJobName, IPrintDocumentAdapter adapter,
PrintAttributes attributes, String packageName, int appId, int userId) {
- adapter = Preconditions.checkNotNull(adapter);
+ Objects.requireNonNull(adapter);
if (!isPrintingEnabled()) {
CharSequence disabledMessage = null;
DevicePolicyManagerInternal dpmi =
@@ -239,7 +240,7 @@
@Override
public Icon getCustomPrinterIcon(PrinterId printerId, int userId) {
- printerId = Preconditions.checkNotNull(printerId);
+ Objects.requireNonNull(printerId);
final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
final UserState userState;
@@ -349,7 +350,7 @@
return;
}
- service = Preconditions.checkNotNull(service);
+ Objects.requireNonNull(service);
final UserState userState;
synchronized (mLock) {
@@ -391,7 +392,7 @@
@Override
public void createPrinterDiscoverySession(IPrinterDiscoveryObserver observer,
int userId) {
- observer = Preconditions.checkNotNull(observer);
+ Objects.requireNonNull(observer);
final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
final UserState userState;
@@ -413,7 +414,7 @@
@Override
public void destroyPrinterDiscoverySession(IPrinterDiscoveryObserver observer,
int userId) {
- observer = Preconditions.checkNotNull(observer);
+ Objects.requireNonNull(observer);
final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
final UserState userState;
@@ -435,7 +436,7 @@
@Override
public void startPrinterDiscovery(IPrinterDiscoveryObserver observer,
List<PrinterId> priorityList, int userId) {
- observer = Preconditions.checkNotNull(observer);
+ Objects.requireNonNull(observer);
if (priorityList != null) {
priorityList = Preconditions.checkCollectionElementsNotNull(priorityList,
"PrinterId");
@@ -460,7 +461,7 @@
@Override
public void stopPrinterDiscovery(IPrinterDiscoveryObserver observer, int userId) {
- observer = Preconditions.checkNotNull(observer);
+ Objects.requireNonNull(observer);
final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
final UserState userState;
@@ -502,7 +503,7 @@
@Override
public void startPrinterStateTracking(PrinterId printerId, int userId) {
- printerId = Preconditions.checkNotNull(printerId);
+ Objects.requireNonNull(printerId);
final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
final UserState userState;
@@ -523,7 +524,7 @@
@Override
public void stopPrinterStateTracking(PrinterId printerId, int userId) {
- printerId = Preconditions.checkNotNull(printerId);
+ Objects.requireNonNull(printerId);
final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
final UserState userState;
@@ -545,7 +546,7 @@
@Override
public void addPrintJobStateChangeListener(IPrintJobStateChangeListener listener,
int appId, int userId) throws RemoteException {
- listener = Preconditions.checkNotNull(listener);
+ Objects.requireNonNull(listener);
final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
final int resolvedAppId;
@@ -569,7 +570,7 @@
@Override
public void removePrintJobStateChangeListener(IPrintJobStateChangeListener listener,
int userId) {
- listener = Preconditions.checkNotNull(listener);
+ Objects.requireNonNull(listener);
final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
final UserState userState;
@@ -591,7 +592,7 @@
@Override
public void addPrintServicesChangeListener(IPrintServicesChangeListener listener,
int userId) throws RemoteException {
- listener = Preconditions.checkNotNull(listener);
+ Objects.requireNonNull(listener);
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PRINT_SERVICES,
null);
@@ -615,7 +616,7 @@
@Override
public void removePrintServicesChangeListener(IPrintServicesChangeListener listener,
int userId) {
- listener = Preconditions.checkNotNull(listener);
+ Objects.requireNonNull(listener);
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PRINT_SERVICES,
null);
@@ -640,7 +641,7 @@
public void addPrintServiceRecommendationsChangeListener(
IRecommendationsChangeListener listener, int userId)
throws RemoteException {
- listener = Preconditions.checkNotNull(listener);
+ Objects.requireNonNull(listener);
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.READ_PRINT_SERVICE_RECOMMENDATIONS, null);
@@ -664,7 +665,7 @@
@Override
public void removePrintServiceRecommendationsChangeListener(
IRecommendationsChangeListener listener, int userId) {
- listener = Preconditions.checkNotNull(listener);
+ Objects.requireNonNull(listener);
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.READ_PRINT_SERVICE_RECOMMENDATIONS, null);
@@ -688,7 +689,7 @@
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- fd = Preconditions.checkNotNull(fd);
+ Objects.requireNonNull(fd);
if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, pw)) return;