Merge "Fix TODO in tuner.java" into rvc-dev
diff --git a/apex/statsd/framework/Android.bp b/apex/statsd/framework/Android.bp
index 986682e..8185bb0 100644
--- a/apex/statsd/framework/Android.bp
+++ b/apex/statsd/framework/Android.bp
@@ -166,8 +166,10 @@
android_test {
name: "FrameworkStatsdTest",
- sdk_version: "module_current",
+ platform_apis: true,
srcs: [
+ // TODO(b/147705194): Use framework-statsd as a lib dependency instead.
+ ":framework-statsd-sources",
"test/**/*.java",
],
manifest: "test/AndroidManifest.xml",
@@ -178,7 +180,6 @@
libs: [
"android.test.runner.stubs",
"android.test.base.stubs",
- "framework-statsd",
],
test_suites: [
"device-tests",
diff --git a/api/current.txt b/api/current.txt
index 4f52862..8152cd6 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -10205,7 +10205,6 @@
field public static final String MEDIA_ROUTER_SERVICE = "media_router";
field public static final String MEDIA_SESSION_SERVICE = "media_session";
field public static final String MIDI_SERVICE = "midi";
- field public static final String MMS_SERVICE = "mms";
field public static final int MODE_APPEND = 32768; // 0x8000
field public static final int MODE_ENABLE_WRITE_AHEAD_LOGGING = 8; // 0x8
field @Deprecated public static final int MODE_MULTI_PROCESS = 4; // 0x4
@@ -47548,11 +47547,6 @@
method @Nullable public android.telephony.mbms.StreamingService startStreaming(android.telephony.mbms.StreamingServiceInfo, @NonNull java.util.concurrent.Executor, android.telephony.mbms.StreamingServiceCallback);
}
- public class MmsManager {
- method public void downloadMultimediaMessage(int, @NonNull String, @NonNull android.net.Uri, @Nullable android.os.Bundle, @Nullable android.app.PendingIntent, long);
- method public void sendMultimediaMessage(int, @NonNull android.net.Uri, @Nullable String, @Nullable android.os.Bundle, @Nullable android.app.PendingIntent, long);
- }
-
@Deprecated public class NeighboringCellInfo implements android.os.Parcelable {
ctor @Deprecated public NeighboringCellInfo();
ctor @Deprecated public NeighboringCellInfo(int, int);
diff --git a/api/system-current.txt b/api/system-current.txt
index b13a420..2d2ebcd 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1871,7 +1871,6 @@
field public static final String ACTION_UPGRADE_SETUP = "android.intent.action.UPGRADE_SETUP";
field public static final String ACTION_USER_ADDED = "android.intent.action.USER_ADDED";
field public static final String ACTION_USER_REMOVED = "android.intent.action.USER_REMOVED";
- field @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public static final String ACTION_USER_SWITCHED = "android.intent.action.USER_SWITCHED";
field public static final String ACTION_VOICE_ASSIST = "android.intent.action.VOICE_ASSIST";
field public static final String CATEGORY_LEANBACK_SETTINGS = "android.intent.category.LEANBACK_SETTINGS";
field public static final String EXTRA_CALLING_PACKAGE = "android.intent.extra.CALLING_PACKAGE";
@@ -4828,11 +4827,11 @@
}
public class Lnb implements java.lang.AutoCloseable {
- method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void close();
- method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int sendDiseqcMessage(@NonNull byte[]);
- method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int setSatellitePosition(int);
- method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int setTone(int);
- method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int setVoltage(int);
+ method public void close();
+ method public int sendDiseqcMessage(@NonNull byte[]);
+ method public int setSatellitePosition(int);
+ method public int setTone(int);
+ method public int setVoltage(int);
field public static final int EVENT_TYPE_DISEQC_RX_OVERFLOW = 0; // 0x0
field public static final int EVENT_TYPE_DISEQC_RX_PARITY_ERROR = 2; // 0x2
field public static final int EVENT_TYPE_DISEQC_RX_TIMEOUT = 1; // 0x1
@@ -4860,32 +4859,32 @@
public class Tuner implements java.lang.AutoCloseable {
ctor @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public Tuner(@NonNull android.content.Context, @Nullable String, int);
- method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int cancelScanning();
- method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int cancelTuning();
- method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void clearOnTuneEventListener();
- method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void clearResourceLostListener();
- method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void close();
- method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int connectCiCam(int);
- method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int disconnectCiCam();
- method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int getAvSyncHwId(@NonNull android.media.tv.tuner.filter.Filter);
- method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public long getAvSyncTime(int);
- method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public static android.media.tv.tuner.DemuxCapabilities getDemuxCapabilities(@NonNull android.content.Context);
- method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public android.media.tv.tuner.frontend.FrontendInfo getFrontendInfo();
+ method public int cancelScanning();
+ method public int cancelTuning();
+ method public void clearOnTuneEventListener();
+ method public void clearResourceLostListener();
+ method public void close();
+ method public int connectCiCam(int);
+ method public int disconnectCiCam();
+ method public int getAvSyncHwId(@NonNull android.media.tv.tuner.filter.Filter);
+ method public long getAvSyncTime(int);
+ method @Nullable public android.media.tv.tuner.DemuxCapabilities getDemuxCapabilities();
+ method @Nullable public android.media.tv.tuner.frontend.FrontendInfo getFrontendInfo();
method @Nullable public android.media.tv.tuner.frontend.FrontendStatus getFrontendStatus(@NonNull int[]);
method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_DESCRAMBLER) public android.media.tv.tuner.Descrambler openDescrambler();
- method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public android.media.tv.tuner.dvr.DvrPlayback openDvrPlayback(long, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.dvr.OnPlaybackStatusChangedListener);
- method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public android.media.tv.tuner.dvr.DvrRecorder openDvrRecorder(long, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.dvr.OnRecordStatusChangedListener);
- method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public android.media.tv.tuner.filter.Filter openFilter(int, int, long, @Nullable java.util.concurrent.Executor, @Nullable android.media.tv.tuner.filter.FilterCallback);
- method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public android.media.tv.tuner.Lnb openLnb(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.LnbCallback);
- method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public android.media.tv.tuner.Lnb openLnbByName(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.LnbCallback);
+ method @Nullable public android.media.tv.tuner.dvr.DvrPlayback openDvrPlayback(long, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.dvr.OnPlaybackStatusChangedListener);
+ method @Nullable public android.media.tv.tuner.dvr.DvrRecorder openDvrRecorder(long, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.dvr.OnRecordStatusChangedListener);
+ method @Nullable public android.media.tv.tuner.filter.Filter openFilter(int, int, long, @Nullable java.util.concurrent.Executor, @Nullable android.media.tv.tuner.filter.FilterCallback);
+ method @Nullable public android.media.tv.tuner.Lnb openLnb(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.LnbCallback);
+ method @Nullable public android.media.tv.tuner.Lnb openLnbByName(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.LnbCallback);
method @Nullable public android.media.tv.tuner.filter.TimeFilter openTimeFilter();
- method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int scan(@NonNull android.media.tv.tuner.frontend.FrontendSettings, int, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.frontend.ScanCallback);
- method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int setLnaEnabled(boolean);
- method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void setOnTuneEventListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.frontend.OnTuneEventListener);
- method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void setResourceLostListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.Tuner.OnResourceLostListener);
- method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void shareFrontendFromTuner(@NonNull android.media.tv.tuner.Tuner);
- method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int tune(@NonNull android.media.tv.tuner.frontend.FrontendSettings);
- method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void updateResourcePriority(int, int);
+ method public int scan(@NonNull android.media.tv.tuner.frontend.FrontendSettings, int, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.frontend.ScanCallback);
+ method public int setLnaEnabled(boolean);
+ method public void setOnTuneEventListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.frontend.OnTuneEventListener);
+ method public void setResourceLostListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.Tuner.OnResourceLostListener);
+ method public void shareFrontendFromTuner(@NonNull android.media.tv.tuner.Tuner);
+ method public int tune(@NonNull android.media.tv.tuner.frontend.FrontendSettings);
+ method public void updateResourcePriority(int, int);
field public static final int INVALID_AV_SYNC_ID = -1; // 0xffffffff
field public static final int INVALID_FILTER_ID = -1; // 0xffffffff
field public static final int INVALID_STREAM_ID = 65535; // 0xffff
@@ -8893,6 +8892,7 @@
method @BinderThread public abstract void onRevokeRuntimePermissions(@NonNull java.util.Map<java.lang.String,java.util.List<java.lang.String>>, boolean, int, @NonNull String, @NonNull java.util.function.Consumer<java.util.Map<java.lang.String,java.util.List<java.lang.String>>>);
method @BinderThread public abstract void onSetRuntimePermissionGrantStateByDeviceAdmin(@NonNull String, @NonNull String, @NonNull String, int, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @BinderThread public void onStageAndApplyRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.InputStream, @NonNull Runnable);
+ method @BinderThread public void onUpdateUserSensitivePermissionFlags(int, @NonNull java.util.concurrent.Executor, @NonNull Runnable);
method @BinderThread public void onUpdateUserSensitivePermissionFlags(int, @NonNull Runnable);
field public static final String SERVICE_INTERFACE = "android.permission.PermissionControllerService";
}
@@ -11571,9 +11571,6 @@
field public static final String ACTION_SIM_APPLICATION_STATE_CHANGED = "android.telephony.action.SIM_APPLICATION_STATE_CHANGED";
field public static final String ACTION_SIM_CARD_STATE_CHANGED = "android.telephony.action.SIM_CARD_STATE_CHANGED";
field public static final String ACTION_SIM_SLOT_STATUS_CHANGED = "android.telephony.action.SIM_SLOT_STATUS_CHANGED";
- field public static final int CARD_POWER_DOWN = 0; // 0x0
- field public static final int CARD_POWER_UP = 1; // 0x1
- field public static final int CARD_POWER_UP_PASS_THROUGH = 2; // 0x2
field public static final int CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES = -2; // 0xfffffffe
field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1
field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 32e815e..37f1a65 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -6832,6 +6832,10 @@
* package will no longer be suspended. The admin can block this by using
* {@link #setUninstallBlocked}.
*
+ * <p>Some apps cannot be suspended, such as device admins, the active launcher, the required
+ * package installer, the required package uninstaller, the required package verifier, the
+ * default dialer, and the permission controller.
+ *
* @param admin The name of the admin component to check, or {@code null} if the caller is a
* package access delegate.
* @param packageNames The package names to suspend or unsuspend.
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 318ae11..c8c8fa6d 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3430,7 +3430,7 @@
TELEPHONY_SUBSCRIPTION_SERVICE,
CARRIER_CONFIG_SERVICE,
EUICC_SERVICE,
- MMS_SERVICE,
+ //@hide: MMS_SERVICE,
TELECOM_SERVICE,
CLIPBOARD_SERVICE,
INPUT_METHOD_SERVICE,
@@ -4344,6 +4344,7 @@
*
* @see #getSystemService(String)
* @see android.telephony.MmsManager
+ * @hide
*/
public static final String MMS_SERVICE = "mms";
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 38c1890..95385ee 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3675,9 +3675,7 @@
* {@link android.Manifest.permission#MANAGE_USERS} to receive this broadcast.
* @hide
*/
- @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
- @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- @SystemApi
+ @UnsupportedAppUsage
public static final String ACTION_USER_SWITCHED =
"android.intent.action.USER_SWITCHED";
diff --git a/core/java/android/permission/PermissionControllerService.java b/core/java/android/permission/PermissionControllerService.java
index 4a42230..82a7d78 100644
--- a/core/java/android/permission/PermissionControllerService.java
+++ b/core/java/android/permission/PermissionControllerService.java
@@ -58,6 +58,7 @@
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
import java.util.function.Consumer;
import java.util.function.IntConsumer;
@@ -214,6 +215,7 @@
@BinderThread
public abstract void onGrantOrUpgradeDefaultRuntimePermissions(@NonNull Runnable callback);
+
/**
* Called by system to update the
* {@link PackageManager}{@code .FLAG_PERMISSION_USER_SENSITIVE_WHEN_*} flags for permissions.
@@ -223,10 +225,22 @@
*
* Typically called by the system when a new app is installed or updated or when creating a
* new user or upgrading either system or permission controller package.
+ *
+ * The callback will be executed by the provided Executor.
+ */
+ @BinderThread
+ public void onUpdateUserSensitivePermissionFlags(int uid, @NonNull Executor executor,
+ @NonNull Runnable callback) {
+ throw new AbstractMethodError("Must be overridden in implementing class");
+ }
+
+ /**
+ * Runs {@link #onUpdateUserSensitivePermissionFlags(int, Executor, Runnable)} with the main
+ * executor.
*/
@BinderThread
public void onUpdateUserSensitivePermissionFlags(int uid, @NonNull Runnable callback) {
- throw new AbstractMethodError("Must be overridden in implementing class");
+ onUpdateUserSensitivePermissionFlags(uid, getMainExecutor(), callback);
}
/**
diff --git a/core/java/android/service/quickaccesswallet/WalletServiceEvent.java b/core/java/android/service/quickaccesswallet/WalletServiceEvent.java
index fb524be..5ee92da 100644
--- a/core/java/android/service/quickaccesswallet/WalletServiceEvent.java
+++ b/core/java/android/service/quickaccesswallet/WalletServiceEvent.java
@@ -40,10 +40,16 @@
public static final int TYPE_NFC_PAYMENT_STARTED = 1;
/**
+ * Indicates that the wallet cards have changed and should be refreshed.
+ * @hide
+ */
+ public static final int TYPE_WALLET_CARDS_UPDATED = 2;
+
+ /**
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
- @IntDef({TYPE_NFC_PAYMENT_STARTED})
+ @IntDef({TYPE_NFC_PAYMENT_STARTED, TYPE_WALLET_CARDS_UPDATED})
public @interface EventType {
}
diff --git a/core/java/android/text/style/ReplacementSpan.java b/core/java/android/text/style/ReplacementSpan.java
index 0553232..9430fd3 100644
--- a/core/java/android/text/style/ReplacementSpan.java
+++ b/core/java/android/text/style/ReplacementSpan.java
@@ -63,7 +63,7 @@
int top, int y, int bottom, @NonNull Paint paint);
/**
- * Gets a brief description of this ImageSpan for use in accessibility support.
+ * Gets a brief description of this ReplacementSpan for use in accessibility support.
*
* @return The content description.
*/
@@ -73,7 +73,7 @@
}
/**
- * Sets the specific content description into ImageSpan.
+ * Sets the specific content description into ReplacementSpan.
* ReplacementSpans are shared with accessibility services,
* but only the content description is available from them.
*
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 5fccf40..4980b33 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -2876,7 +2876,7 @@
}
/**
- * Replaces any ClickableSpans in mText with placeholders.
+ * Replaces any ClickableSpan in the given {@code text} with placeholders.
*
* @param text The text.
*
@@ -2910,7 +2910,7 @@
}
/**
- * Replace any ImageSpans in mText with its content description.
+ * Replaces any ReplacementSpan in the given {@code text} if the object has content description.
*
* @param text The text.
*
diff --git a/core/java/com/android/ims/internal/uce/uceservice/ImsUceManager.java b/core/java/com/android/ims/internal/uce/uceservice/ImsUceManager.java
index 9aee879f..ef8d018 100644
--- a/core/java/com/android/ims/internal/uce/uceservice/ImsUceManager.java
+++ b/core/java/com/android/ims/internal/uce/uceservice/ImsUceManager.java
@@ -50,6 +50,16 @@
public static final String ACTION_UCE_SERVICE_DOWN =
"com.android.ims.internal.uce.UCE_SERVICE_DOWN";
+ /**
+ * Uce Service status received in IUceListener.setStatus() callback
+ */
+ public static final int UCE_SERVICE_STATUS_FAILURE = 0;
+ /** indicate UI to call Presence/Options API. */
+ public static final int UCE_SERVICE_STATUS_ON = 1;
+ /** Indicate UI destroy Presence/Options */
+ public static final int UCE_SERVICE_STATUS_CLOSED = 2;
+ /** Service up and trying to register for network events */
+ public static final int UCE_SERVICE_STATUS_READY = 3;
/**
* Gets the instance of UCE Manager
diff --git a/media/java/android/media/AudioMetadata.java b/media/java/android/media/AudioMetadata.java
index e67ba59..1a9517c 100644
--- a/media/java/android/media/AudioMetadata.java
+++ b/media/java/android/media/AudioMetadata.java
@@ -41,43 +41,47 @@
private static final String TAG = "AudioMetadata";
/**
- * Key interface for the map.
+ * Key interface for the {@code AudioMetadata} map.
*
- * The presence of this {@code Key} interface on an object allows
- * it to be used to reference metadata in the Audio Framework.
+ * <p>The presence of this {@code Key} interface on an object allows
+ * it to reference metadata in the Audio Framework.</p>
+ *
+ * <p>Vendors are allowed to implement this {@code Key} interface for their debugging or
+ * private application use. To avoid name conflicts, vendor key names should be qualified by
+ * the vendor company name followed by a dot; for example, "vendorCompany.someVolume".</p>
*
* @param <T> type of value associated with {@code Key}.
*/
- // Conceivably metadata keys exposing multiple interfaces
- // could be eligible to work in multiple framework domains.
+ /*
+ * Internal details:
+ * Conceivably metadata keys exposing multiple interfaces
+ * could be eligible to work in multiple framework domains.
+ */
public interface Key<T> {
/**
- * Returns the internal name of the key.
+ * Returns the internal name of the key. The name should be unique in the
+ * {@code AudioMetadata} namespace. Vendors should prefix their keys with
+ * the company name followed by a dot.
*/
@NonNull
String getName();
/**
- * Returns the class type of the associated value.
+ * Returns the class type {@code T} of the associated value. Valid class types for
+ * {@link android.os.Build.VERSION_CODES#R} are
+ * {@code Integer.class}, {@code Long.class}, {@code Float.class}, {@code Double.class},
+ * {@code String.class}.
*/
@NonNull
Class<T> getValueClass();
// TODO: consider adding bool isValid(@NonNull T value)
-
- /**
- * Do not allow non-framework apps to create their own keys
- * by implementing this interface; keep a method hidden.
- *
- * @hide
- */
- boolean isFromFramework();
}
/**
* A read only {@code Map} interface of {@link Key} value pairs.
*
- * Using a {@link Key} interface, look up the corresponding value.
+ * <p>Using a {@link Key} interface, the map looks up the corresponding value.</p>
*/
public interface ReadMap {
/**
@@ -301,12 +305,6 @@
return mType;
}
- // hidden interface method to prevent user class implements the of Key interface.
- @Override
- public boolean isFromFramework() {
- return true;
- }
-
/**
* Return true if the name and the type of two objects are the same.
*/
diff --git a/media/java/android/media/tv/tuner/Lnb.java b/media/java/android/media/tv/tuner/Lnb.java
index 525ee4d..9ce895e 100644
--- a/media/java/android/media/tv/tuner/Lnb.java
+++ b/media/java/android/media/tv/tuner/Lnb.java
@@ -19,7 +19,6 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.content.Context;
import android.hardware.tv.tuner.V1_0.Constants;
@@ -173,10 +172,8 @@
* @param voltage the power voltage constant the Lnb to use.
* @return result status of the operation.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@Result
public int setVoltage(@Voltage int voltage) {
- TunerUtils.checkTunerPermission(mContext);
return nativeSetVoltage(voltage);
}
@@ -186,10 +183,8 @@
* @param tone the tone mode the Lnb to use.
* @return result status of the operation.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@Result
public int setTone(@Tone int tone) {
- TunerUtils.checkTunerPermission(mContext);
return nativeSetTone(tone);
}
@@ -199,10 +194,8 @@
* @param position the position the Lnb to use.
* @return result status of the operation.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@Result
public int setSatellitePosition(@Position int position) {
- TunerUtils.checkTunerPermission(mContext);
return nativeSetSatellitePosition(position);
}
@@ -216,19 +209,15 @@
*
* @return result status of the operation.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@Result
public int sendDiseqcMessage(@NonNull byte[] message) {
- TunerUtils.checkTunerPermission(mContext);
return nativeSendDiseqcMessage(message);
}
/**
* Releases the LNB instance.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
public void close() {
- TunerUtils.checkTunerPermission(mContext);
nativeClose();
}
}
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 3954abc..3eb77d5 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -43,6 +43,8 @@
import android.media.tv.tuner.frontend.OnTuneEventListener;
import android.media.tv.tuner.frontend.ScanCallback;
import android.media.tv.tunerresourcemanager.ResourceClientProfile;
+import android.media.tv.tunerresourcemanager.TunerDemuxRequest;
+import android.media.tv.tunerresourcemanager.TunerDescramblerRequest;
import android.media.tv.tunerresourcemanager.TunerFrontendRequest;
import android.media.tv.tunerresourcemanager.TunerLnbRequest;
import android.media.tv.tunerresourcemanager.TunerResourceManager;
@@ -218,6 +220,8 @@
@Nullable
private Executor mOnResourceLostListenerExecutor;
+ private Integer mDemuxHandle;
+ private Integer mDescramblerHandle;
private final TunerResourceManager.ResourcesReclaimListener mResourceListener =
new TunerResourceManager.ResourcesReclaimListener() {
@@ -255,7 +259,6 @@
* @param executor the executor on which the listener should be invoked.
* @param listener the listener that will be run.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
public void setResourceLostListener(@NonNull @CallbackExecutor Executor executor,
@NonNull OnResourceLostListener listener) {
Objects.requireNonNull(executor, "OnResourceLostListener must not be null");
@@ -267,7 +270,6 @@
/**
* Removes the listener for resource lost.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
public void clearResourceLostListener() {
mOnResourceLostListener = null;
mOnResourceLostListenerExecutor = null;
@@ -278,7 +280,6 @@
*
* @param tuner the Tuner instance to share frontend resource with.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
public void shareFrontendFromTuner(@NonNull Tuner tuner) {
mTunerResourceManager.shareFrontend(mClientId, tuner.mClientId);
mFrontendHandle = tuner.mFrontendHandle;
@@ -296,7 +297,6 @@
* @param priority the new priority.
* @param niceValue the nice value.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
public void updateResourcePriority(int priority, int niceValue) {
mTunerResourceManager.updateClientPriority(mClientId, priority, niceValue);
}
@@ -306,7 +306,6 @@
/**
* Releases the Tuner instance.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@Override
public void close() {
if (mFrontendHandle != null) {
@@ -438,10 +437,8 @@
* @throws SecurityException if the caller does not have appropriate permissions.
* @see #tune(FrontendSettings)
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
public void setOnTuneEventListener(@NonNull @CallbackExecutor Executor executor,
@NonNull OnTuneEventListener eventListener) {
- TunerUtils.checkTunerPermission(mContext);
mOnTuneEventListener = eventListener;
mOnTunerEventExecutor = executor;
}
@@ -452,7 +449,6 @@
* @throws SecurityException if the caller does not have appropriate permissions.
* @see #setOnTuneEventListener(Executor, OnTuneEventListener)
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
public void clearOnTuneEventListener() {
TunerUtils.checkTunerPermission(mContext);
mOnTuneEventListener = null;
@@ -483,7 +479,6 @@
* @throws SecurityException if the caller does not have appropriate permissions.
* @see #setOnTuneEventListener(Executor, OnTuneEventListener)
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@Result
public int tune(@NonNull FrontendSettings settings) {
mFrontendType = settings.getType();
@@ -500,7 +495,6 @@
*
* @return result status of the operation.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@Result
public int cancelTuning() {
TunerUtils.checkTunerPermission(mContext);
@@ -518,7 +512,6 @@
* @throws IllegalStateException if {@code scan} is called again before
* {@link #cancelScanning()} is called.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@Result
public int scan(@NonNull FrontendSettings settings, @ScanType int scanType,
@NonNull @CallbackExecutor Executor executor, @NonNull ScanCallback scanCallback) {
@@ -546,10 +539,8 @@
*
* @throws SecurityException if the caller does not have appropriate permissions.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@Result
public int cancelScanning() {
- TunerUtils.checkTunerPermission(mContext);
int retVal = nativeStopScan();
mScanCallback = null;
mScanCallbackExecutor = null;
@@ -557,11 +548,11 @@
}
private boolean requestFrontend() {
- int[] feId = new int[1];
+ int[] feHandle = new int[1];
TunerFrontendRequest request = new TunerFrontendRequest(mClientId, mFrontendType);
- boolean granted = mTunerResourceManager.requestFrontend(request, feId);
+ boolean granted = mTunerResourceManager.requestFrontend(request, feHandle);
if (granted) {
- mFrontendHandle = feId[0];
+ mFrontendHandle = feHandle[0];
}
return granted;
}
@@ -588,10 +579,8 @@
*
* @return result status of the operation.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@Result
public int setLnaEnabled(boolean enable) {
- TunerUtils.checkTunerPermission(mContext);
return nativeSetLna(enable);
}
@@ -614,9 +603,8 @@
* @param filter the filter instance for the hardware sync ID.
* @return the id of hardware A/V sync.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
public int getAvSyncHwId(@NonNull Filter filter) {
- TunerUtils.checkTunerPermission(mContext);
+ checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX);
Integer id = nativeGetAvSyncHwId(filter);
return id == null ? INVALID_AV_SYNC_ID : id;
}
@@ -630,9 +618,8 @@
* @param avSyncHwId the hardware id of A/V sync.
* @return the current timestamp of hardware A/V sync.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
public long getAvSyncTime(int avSyncHwId) {
- TunerUtils.checkTunerPermission(mContext);
+ checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX);
Long time = nativeGetAvSyncTime(avSyncHwId);
return time == null ? INVALID_TIMESTAMP : time;
}
@@ -646,10 +633,9 @@
* @param ciCamId specify CI-CAM Id to connect.
* @return result status of the operation.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@Result
public int connectCiCam(int ciCamId) {
- TunerUtils.checkTunerPermission(mContext);
+ checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX);
return nativeConnectCiCam(ciCamId);
}
@@ -660,10 +646,9 @@
*
* @return result status of the operation.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@Result
public int disconnectCiCam() {
- TunerUtils.checkTunerPermission(mContext);
+ checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX);
return nativeDisconnectCiCam();
}
@@ -672,10 +657,9 @@
*
* @return The frontend information. {@code null} if the operation failed.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@Nullable
public FrontendInfo getFrontendInfo() {
- TunerUtils.checkTunerPermission(mContext);
+ checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND);
if (mFrontend == null) {
throw new IllegalStateException("frontend is not initialized");
}
@@ -688,14 +672,11 @@
/**
* Gets Demux capabilities.
*
- * @param context the context of the caller.
* @return A {@link DemuxCapabilities} instance that represents the demux capabilities.
* {@code null} if the operation failed.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@Nullable
- public static DemuxCapabilities getDemuxCapabilities(@NonNull Context context) {
- TunerUtils.checkTunerPermission(context);
+ public DemuxCapabilities getDemuxCapabilities() {
return nativeGetDemuxCapabilities();
}
@@ -801,12 +782,11 @@
* @param cb the callback to receive notifications from filter.
* @return the opened filter. {@code null} if the operation failed.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@Nullable
public Filter openFilter(@Type int mainType, @Subtype int subType,
@BytesLong long bufferSize, @CallbackExecutor @Nullable Executor executor,
@Nullable FilterCallback cb) {
- TunerUtils.checkTunerPermission(mContext);
+ checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX);
Filter filter = nativeOpenFilter(
mainType, TunerUtils.getFilterSubtype(mainType, subType), bufferSize);
if (filter != null) {
@@ -830,7 +810,6 @@
* @param cb the callback to receive notifications from LNB.
* @return the opened LNB object. {@code null} if the operation failed.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@Nullable
public Lnb openLnb(@CallbackExecutor @NonNull Executor executor, @NonNull LnbCallback cb) {
Objects.requireNonNull(executor, "executor must not be null");
@@ -848,23 +827,22 @@
* @param cb the callback to receive notifications from LNB.
* @return the opened LNB object. {@code null} if the operation failed.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@Nullable
public Lnb openLnbByName(@NonNull String name, @CallbackExecutor @NonNull Executor executor,
@NonNull LnbCallback cb) {
Objects.requireNonNull(name, "LNB name must not be null");
Objects.requireNonNull(executor, "executor must not be null");
Objects.requireNonNull(cb, "LnbCallback must not be null");
- TunerUtils.checkTunerPermission(mContext);
+ checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_LNB);
return nativeOpenLnbByName(name);
}
private boolean requestLnb() {
- int[] lnbId = new int[1];
+ int[] lnbHandle = new int[1];
TunerLnbRequest request = new TunerLnbRequest(mClientId);
- boolean granted = mTunerResourceManager.requestLnb(request, lnbId);
+ boolean granted = mTunerResourceManager.requestLnb(request, lnbHandle);
if (granted) {
- mLnbHandle = lnbId[0];
+ mLnbHandle = lnbHandle[0];
}
return granted;
}
@@ -876,6 +854,7 @@
*/
@Nullable
public TimeFilter openTimeFilter() {
+ checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX);
return nativeOpenTimeFilter();
}
@@ -893,7 +872,7 @@
@RequiresPermission(android.Manifest.permission.ACCESS_TV_DESCRAMBLER)
@Nullable
public Descrambler openDescrambler() {
- TunerUtils.checkDescramblerPermission(mContext);
+ checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DESCRAMBLER);
return nativeOpenDescrambler();
}
@@ -907,7 +886,6 @@
* @param l the listener to receive notifications from DVR recorder.
* @return the opened DVR recorder object. {@code null} if the operation failed.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@Nullable
public DvrRecorder openDvrRecorder(
@BytesLong long bufferSize,
@@ -915,7 +893,7 @@
@NonNull OnRecordStatusChangedListener l) {
Objects.requireNonNull(executor, "executor must not be null");
Objects.requireNonNull(l, "OnRecordStatusChangedListener must not be null");
- TunerUtils.checkTunerPermission(mContext);
+ checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX);
DvrRecorder dvr = nativeOpenDvrRecorder(bufferSize);
return dvr;
}
@@ -930,7 +908,6 @@
* @param l the listener to receive notifications from DVR recorder.
* @return the opened DVR playback object. {@code null} if the operation failed.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@Nullable
public DvrPlayback openDvrPlayback(
@BytesLong long bufferSize,
@@ -938,13 +915,32 @@
@NonNull OnPlaybackStatusChangedListener l) {
Objects.requireNonNull(executor, "executor must not be null");
Objects.requireNonNull(l, "OnPlaybackStatusChangedListener must not be null");
- TunerUtils.checkTunerPermission(mContext);
+ checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX);
DvrPlayback dvr = nativeOpenDvrPlayback(bufferSize);
return dvr;
}
+ private boolean requestDemux() {
+ int[] demuxHandle = new int[1];
+ TunerDemuxRequest request = new TunerDemuxRequest(mClientId);
+ boolean granted = mTunerResourceManager.requestDemux(request, demuxHandle);
+ if (granted) {
+ mDemuxHandle = demuxHandle[0];
+ }
+ return granted;
+ }
+
+ private boolean requestDescrambler() {
+ int[] descramblerHandle = new int[1];
+ TunerDescramblerRequest request = new TunerDescramblerRequest(mClientId);
+ boolean granted = mTunerResourceManager.requestDescrambler(request, descramblerHandle);
+ if (granted) {
+ mDescramblerHandle = descramblerHandle[0];
+ }
+ return granted;
+ }
+
private boolean checkResource(int resourceType) {
- // TODO: demux and descrambler
switch (resourceType) {
case TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND: {
if (mFrontendHandle == null && !requestFrontend()) {
@@ -958,6 +954,18 @@
}
break;
}
+ case TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX: {
+ if (mDemuxHandle == null && !requestDemux()) {
+ return false;
+ }
+ break;
+ }
+ case TunerResourceManager.TUNER_RESOURCE_TYPE_DESCRAMBLER: {
+ if (mDescramblerHandle == null && !requestDescrambler()) {
+ return false;
+ }
+ break;
+ }
}
return true;
}
diff --git a/media/java/android/media/tv/tuner/TunerUtils.java b/media/java/android/media/tv/tuner/TunerUtils.java
index c3be12a..a41b397 100644
--- a/media/java/android/media/tv/tuner/TunerUtils.java
+++ b/media/java/android/media/tv/tuner/TunerUtils.java
@@ -60,6 +60,7 @@
* @throws SecurityException if the caller doesn't have the permission.
*/
public static void checkPermission(Context context, String permission) {
+ // TODO: remove checkPermission methods
if (context.checkCallingOrSelfPermission(permission)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Caller must have " + permission + " permission.");
diff --git a/mms/java/android/telephony/MmsManager.java b/mms/java/android/telephony/MmsManager.java
index f07cd5e..6e47741 100644
--- a/mms/java/android/telephony/MmsManager.java
+++ b/mms/java/android/telephony/MmsManager.java
@@ -32,6 +32,7 @@
/**
* Manages MMS operations such as sending multimedia messages.
* Get this object by calling Context#getSystemService(Context#MMS_SERVICE).
+ * @hide
*/
@SystemService(Context.MMS_SERVICE)
public class MmsManager {
diff --git a/packages/SystemUI/res/layout/controls_no_favorites.xml b/packages/SystemUI/res/layout/controls_no_favorites.xml
index 8074efd..74fc167 100644
--- a/packages/SystemUI/res/layout/controls_no_favorites.xml
+++ b/packages/SystemUI/res/layout/controls_no_favorites.xml
@@ -40,14 +40,20 @@
android:paddingBottom="8dp" />
<TextView
+ style="@style/TextAppearance.ControlSetup.Title"
android:id="@+id/controls_title"
android:text="@string/quick_controls_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:singleLine="true"
- android:layout_gravity="center"
- android:textSize="25sp"
- android:textColor="@*android:color/foreground_material_dark"
- android:fontFamily="@*android:string/config_headlineFontFamily" />
+ android:layout_gravity="center" />
+
+ <TextView
+ style="@style/TextAppearance.ControlSetup.Subtitle"
+ android:id="@+id/controls_subtitle"
+ android:visibility="gone"
+ android:layout_marginTop="12dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center" />
</LinearLayout>
</merge>
diff --git a/packages/SystemUI/res/layout/status_bar_notification_section_header.xml b/packages/SystemUI/res/layout/status_bar_notification_section_header.xml
index 44c409e..b5822c8 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_section_header.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_section_header.xml
@@ -35,7 +35,34 @@
android:forceHasOverlappingRendering="false"
android:clipChildren="false"
>
- <include layout="@layout/status_bar_notification_section_header_contents"/>
+ <FrameLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="start|center_vertical"
+ android:layout_weight="1">
+
+ <TextView
+ style="@style/TextAppearance.NotificationSectionHeaderButton"
+ android:id="@+id/header_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:forceHasOverlappingRendering="false"
+ android:text="@string/notification_section_header_gentle"
+ />
+
+ </FrameLayout>
+ <ImageView
+ android:id="@+id/btn_clear_all"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:src="@drawable/status_bar_notification_section_header_clear_btn"
+ android:contentDescription="@string/accessibility_notification_section_header_gentle_clear_all"
+ android:scaleType="center"
+ android:tint="?attr/wallpaperTextColor"
+ android:tintMode="src_in"
+ android:visibility="gone"
+ android:forceHasOverlappingRendering="false"
+ />
</LinearLayout>
</com.android.systemui.statusbar.notification.stack.SectionHeaderView>
diff --git a/packages/SystemUI/res/layout/status_bar_notification_section_header_contents.xml b/packages/SystemUI/res/layout/status_bar_notification_section_header_contents.xml
deleted file mode 100644
index 3b9c44d..0000000
--- a/packages/SystemUI/res/layout/status_bar_notification_section_header_contents.xml
+++ /dev/null
@@ -1,47 +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
- -->
-
-<!-- Used by both status_bar_notification_header and SectionHeaderView -->
-<merge xmlns:android="http://schemas.android.com/apk/res/android" >
- <FrameLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:gravity="start|center_vertical"
- android:layout_weight="1">
-
- <TextView
- style="@style/TextAppearance.NotificationSectionHeaderButton"
- android:id="@+id/header_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:forceHasOverlappingRendering="false"
- android:text="@string/notification_section_header_gentle"
- />
-
- </FrameLayout>
- <ImageView
- android:id="@+id/btn_clear_all"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:src="@drawable/status_bar_notification_section_header_clear_btn"
- android:contentDescription="@string/accessibility_notification_section_header_gentle_clear_all"
- android:scaleType="center"
- android:tint="?attr/wallpaperTextColor"
- android:tintMode="src_in"
- android:visibility="gone"
- android:forceHasOverlappingRendering="false"
- />
-</merge>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 06e027d..4d6b759 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -532,4 +532,8 @@
<!-- Respect drawable/rounded.xml intrinsic size for multiple radius corner path customization -->
<bool name="config_roundedCornerMultipleRadius">false</bool>
+ <!-- Controls can query a preferred application for limited number of suggested controls.
+ This config value should contain the package name of that preferred application.
+ -->
+ <string translatable="false" name="config_controlsPreferredPackage"></string>
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 93bafdb..cb08840 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2657,4 +2657,8 @@
<!-- Tooltip to show in management screen when there are multiple structures [CHAR_LIMIT=50] -->
<string name="controls_structure_tooltip">Swipe to see other structures</string>
+
+ <!-- Message to tell the user to wait while systemui attempts to load a set of
+ recommended controls [CHAR_LIMIT=30] -->
+ <string name="controls_seeding_in_progress">Loading recommendations</string>
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 20b88a1..1283fe0 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -699,6 +699,20 @@
<item name="*android:colorPopupBackground">@color/control_list_popup_background</item>
</style>
+ <style name="TextAppearance.ControlSetup">
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ <item name="android:textColor">@color/control_primary_text</item>
+ <item name="android:singleLine">true</item>
+ </style>
+
+ <style name="TextAppearance.ControlSetup.Title">
+ <item name="android:textSize">25sp</item>
+ </style>
+
+ <style name="TextAppearance.ControlSetup.Subtitle">
+ <item name="android:textSize">16sp</item>
+ </style>
+
<style name="Theme.ControlsRequestDialog" parent="@style/Theme.SystemUI.MediaProjectionAlertDialog"/>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt
index c5af436..d4d4d2a 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt
@@ -43,6 +43,14 @@
fun bindAndLoad(component: ComponentName, callback: LoadCallback): Runnable
/**
+ * Request bind to a service and load a limited number of suggested controls.
+ *
+ * @param component The [ComponentName] of the service to bind
+ * @param callback a callback to return the loaded controls to (or an error).
+ */
+ fun bindAndLoadSuggested(component: ComponentName, callback: LoadCallback)
+
+ /**
* Request to bind to the given service.
*
* @param component The [ComponentName] of the service to bind
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
index f8d4a39..5d03fc5 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
@@ -44,6 +44,8 @@
companion object {
private const val TAG = "ControlsBindingControllerImpl"
+ private const val MAX_CONTROLS_REQUEST = 100000L
+ private const val SUGGESTED_CONTROLS_REQUEST = 4L
}
private var currentUser = UserHandle.of(ActivityManager.getCurrentUser())
@@ -97,24 +99,37 @@
component: ComponentName,
callback: ControlsBindingController.LoadCallback
): Runnable {
- val subscriber = LoadSubscriber(callback)
+ val subscriber = LoadSubscriber(callback, MAX_CONTROLS_REQUEST)
retrieveLifecycleManager(component).maybeBindAndLoad(subscriber)
return subscriber.loadCancel()
}
+ override fun bindAndLoadSuggested(
+ component: ComponentName,
+ callback: ControlsBindingController.LoadCallback
+ ) {
+ val subscriber = LoadSubscriber(callback, SUGGESTED_CONTROLS_REQUEST)
+ retrieveLifecycleManager(component).maybeBindAndLoadSuggested(subscriber)
+ }
+
override fun subscribe(structureInfo: StructureInfo) {
// make sure this has happened. only allow one active subscription
unsubscribe()
- statefulControlSubscriber = null
val provider = retrieveLifecycleManager(structureInfo.componentName)
- val scs = StatefulControlSubscriber(lazyController.get(), provider, backgroundExecutor)
+ val scs = StatefulControlSubscriber(
+ lazyController.get(),
+ provider,
+ backgroundExecutor,
+ MAX_CONTROLS_REQUEST
+ )
statefulControlSubscriber = scs
provider.maybeBindAndSubscribe(structureInfo.controls.map { it.controlId }, scs)
}
override fun unsubscribe() {
statefulControlSubscriber?.cancel()
+ statefulControlSubscriber = null
}
override fun action(
@@ -201,10 +216,11 @@
private inner class OnSubscribeRunnable(
token: IBinder,
- val subscription: IControlsSubscription
+ val subscription: IControlsSubscription,
+ val requestLimit: Long
) : CallbackRunnable(token) {
override fun doRun() {
- provider?.startSubscription(subscription)
+ provider?.startSubscription(subscription, requestLimit)
}
}
@@ -234,7 +250,8 @@
}
private inner class LoadSubscriber(
- val callback: ControlsBindingController.LoadCallback
+ val callback: ControlsBindingController.LoadCallback,
+ val requestLimit: Long
) : IControlsSubscriber.Stub() {
val loadedControls = ArrayList<Control>()
var hasError = false
@@ -246,7 +263,7 @@
override fun onSubscribe(token: IBinder, subs: IControlsSubscription) {
_loadCancelInternal = subs::cancel
- backgroundExecutor.execute(OnSubscribeRunnable(token, subs))
+ backgroundExecutor.execute(OnSubscribeRunnable(token, subs, requestLimit))
}
override fun onNext(token: IBinder, c: Control) {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
index 9e0d26c..ae75dd4 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
@@ -114,6 +114,25 @@
// FAVORITE MANAGEMENT
/**
+ * Send a request to seed favorites into the persisted XML file
+ *
+ * @param componentName the component to seed controls from
+ * @param callback true if the favorites were persisted
+ */
+ fun seedFavoritesForComponent(
+ componentName: ComponentName,
+ callback: Consumer<Boolean>
+ )
+
+ /**
+ * Callback to be informed when the seeding process has finished
+ *
+ * @param callback consumer accepts true if successful
+ * @return true if seeding is in progress and the callback was added
+ */
+ fun addSeedingFavoritesCallback(callback: Consumer<Boolean>): Boolean
+
+ /**
* Get all the favorites.
*
* @return a list of the structures that have at least one favorited control
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
index 9cb902f..2e34ea5 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
@@ -31,6 +31,7 @@
import android.provider.Settings
import android.service.controls.Control
import android.service.controls.actions.ControlAction
+import android.util.ArrayMap
import android.util.Log
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.Dumpable
@@ -74,6 +75,9 @@
private var loadCanceller: Runnable? = null
+ private var seedingInProgress = false
+ private val seedingCallbacks = mutableListOf<Consumer<Boolean>>()
+
private var currentUser = UserHandle.of(ActivityManager.getCurrentUser())
override val currentUserId
get() = currentUser.identifier
@@ -280,6 +284,84 @@
)
}
+ override fun addSeedingFavoritesCallback(callback: Consumer<Boolean>): Boolean {
+ if (!seedingInProgress) return false
+ executor.execute {
+ // status may have changed by this point, so check again and inform the
+ // caller if necessary
+ if (seedingInProgress) seedingCallbacks.add(callback)
+ else callback.accept(false)
+ }
+ return true
+ }
+
+ override fun seedFavoritesForComponent(
+ componentName: ComponentName,
+ callback: Consumer<Boolean>
+ ) {
+ Log.i(TAG, "Beginning request to seed favorites for: $componentName")
+ if (!confirmAvailability()) {
+ if (userChanging) {
+ // Try again later, userChanging should not last forever. If so, we have bigger
+ // problems. This will return a runnable that allows to cancel the delayed version,
+ // it will not be able to cancel the load if
+ executor.executeDelayed(
+ { seedFavoritesForComponent(componentName, callback) },
+ USER_CHANGE_RETRY_DELAY,
+ TimeUnit.MILLISECONDS
+ )
+ } else {
+ callback.accept(false)
+ }
+ return
+ }
+ seedingInProgress = true
+ bindingController.bindAndLoadSuggested(
+ componentName,
+ object : ControlsBindingController.LoadCallback {
+ override fun accept(controls: List<Control>) {
+ executor.execute {
+ val structureToControls =
+ ArrayMap<CharSequence, MutableList<ControlInfo>>()
+
+ controls.forEach {
+ val structure = it.structure ?: ""
+ val list = structureToControls.get(structure)
+ ?: mutableListOf<ControlInfo>()
+ list.add(ControlInfo(it.controlId, it.title, it.deviceType))
+ structureToControls.put(structure, list)
+ }
+
+ structureToControls.forEach {
+ (s, cs) -> Favorites.replaceControls(
+ StructureInfo(componentName, s, cs))
+ }
+
+ persistenceWrapper.storeFavorites(Favorites.getAllStructures())
+ callback.accept(true)
+ endSeedingCall(true)
+ }
+ }
+
+ override fun error(message: String) {
+ Log.e(TAG, "Unable to seed favorites: $message")
+ executor.execute {
+ callback.accept(false)
+ endSeedingCall(false)
+ }
+ }
+ }
+ )
+ }
+
+ private fun endSeedingCall(state: Boolean) {
+ seedingInProgress = false
+ seedingCallbacks.forEach {
+ it.accept(state)
+ }
+ seedingCallbacks.clear()
+ }
+
override fun cancelLoad() {
loadCanceller?.let {
executor.execute(it)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
index 4918bd7..209d056 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
@@ -66,22 +66,17 @@
@GuardedBy("subscriptions")
private val subscriptions = mutableListOf<IControlsSubscription>()
private var requiresBound = false
- @GuardedBy("queuedMessages")
- private val queuedMessages: MutableSet<Message> = ArraySet()
+ @GuardedBy("queuedServiceMethods")
+ private val queuedServiceMethods: MutableSet<ServiceMethod> = ArraySet()
private var wrapper: ServiceWrapper? = null
private var bindTryCount = 0
private val TAG = javaClass.simpleName
private var onLoadCanceller: Runnable? = null
companion object {
- private const val MSG_LOAD = 0
- private const val MSG_SUBSCRIBE = 1
- private const val MSG_ACTION = 2
- private const val MSG_UNBIND = 3
private const val BIND_RETRY_DELAY = 1000L // ms
private const val LOAD_TIMEOUT_SECONDS = 30L // seconds
private const val MAX_BIND_RETRIES = 5
- private const val MAX_CONTROLS_REQUEST = 100000L
private const val DEBUG = true
private val BIND_FLAGS = Context.BIND_AUTO_CREATE or Context.BIND_FOREGROUND_SERVICE or
Context.BIND_WAIVE_PRIORITY
@@ -130,7 +125,7 @@
try {
service.linkToDeath(this@ControlsProviderLifecycleManager, 0)
} catch (_: RemoteException) {}
- handlePendingMessages()
+ handlePendingServiceMethods()
}
override fun onServiceDisconnected(name: ComponentName?) {
@@ -140,29 +135,14 @@
}
}
- private fun handlePendingMessages() {
- val queue = synchronized(queuedMessages) {
- ArraySet(queuedMessages).also {
- queuedMessages.clear()
+ private fun handlePendingServiceMethods() {
+ val queue = synchronized(queuedServiceMethods) {
+ ArraySet(queuedServiceMethods).also {
+ queuedServiceMethods.clear()
}
}
- if (Message.Unbind in queue) {
- bindService(false)
- return
- }
-
- queue.filter { it is Message.Load }.forEach {
- val msg = it as Message.Load
- load(msg.subscriber)
- }
-
- queue.filter { it is Message.Subscribe }.forEach {
- val msg = it as Message.Subscribe
- subscribe(msg.list, msg.subscriber)
- }
- queue.filter { it is Message.Action }.forEach {
- val msg = it as Message.Action
- action(msg.id, msg.action)
+ queue.forEach {
+ it.run()
}
}
@@ -177,33 +157,17 @@
}
}
- private fun queueMessage(message: Message) {
- synchronized(queuedMessages) {
- queuedMessages.add(message)
+ private fun queueServiceMethod(sm: ServiceMethod) {
+ synchronized(queuedServiceMethods) {
+ queuedServiceMethods.add(sm)
}
}
- private fun unqueueMessageType(type: Int) {
- synchronized(queuedMessages) {
- queuedMessages.removeIf { it.type == type }
- }
- }
-
- private fun load(subscriber: IControlsSubscriber.Stub) {
- if (DEBUG) {
- Log.d(TAG, "load $componentName")
- }
- if (!(wrapper?.load(subscriber) ?: false)) {
- queueMessage(Message.Load(subscriber))
- binderDied()
- }
- }
-
- private inline fun invokeOrQueue(f: () -> Unit, msg: Message) {
+ private fun invokeOrQueue(sm: ServiceMethod) {
wrapper?.run {
- f()
+ sm.run()
} ?: run {
- queueMessage(msg)
+ queueServiceMethod(sm)
bindService(true)
}
}
@@ -217,7 +181,6 @@
* @param subscriber the subscriber that manages coordination for loading controls
*/
fun maybeBindAndLoad(subscriber: IControlsSubscriber.Stub) {
- unqueueMessageType(MSG_UNBIND)
onLoadCanceller = executor.executeDelayed({
// Didn't receive a response in time, log and send back error
Log.d(TAG, "Timeout waiting onLoad for $componentName")
@@ -225,7 +188,26 @@
unbindService()
}, LOAD_TIMEOUT_SECONDS, TimeUnit.SECONDS)
- invokeOrQueue({ load(subscriber) }, Message.Load(subscriber))
+ invokeOrQueue(Load(subscriber))
+ }
+
+ /**
+ * Request a call to [IControlsProvider.loadSuggested].
+ *
+ * If the service is not bound, the call will be queued and the service will be bound first.
+ * The service will be unbound after the controls are returned or the call times out.
+ *
+ * @param subscriber the subscriber that manages coordination for loading controls
+ */
+ fun maybeBindAndLoadSuggested(subscriber: IControlsSubscriber.Stub) {
+ onLoadCanceller = executor.executeDelayed({
+ // Didn't receive a response in time, log and send back error
+ Log.d(TAG, "Timeout waiting onLoadSuggested for $componentName")
+ subscriber.onError(token, "Timeout waiting onLoadSuggested")
+ unbindService()
+ }, LOAD_TIMEOUT_SECONDS, TimeUnit.SECONDS)
+
+ invokeOrQueue(Suggest(subscriber))
}
fun cancelLoadTimeout() {
@@ -240,23 +222,8 @@
*
* @param controlIds a list of the ids of controls to send status back.
*/
- fun maybeBindAndSubscribe(controlIds: List<String>, subscriber: IControlsSubscriber) {
- invokeOrQueue(
- { subscribe(controlIds, subscriber) },
- Message.Subscribe(controlIds, subscriber)
- )
- }
-
- private fun subscribe(controlIds: List<String>, subscriber: IControlsSubscriber) {
- if (DEBUG) {
- Log.d(TAG, "subscribe $componentName - $controlIds")
- }
-
- if (!(wrapper?.subscribe(controlIds, subscriber) ?: false)) {
- queueMessage(Message.Subscribe(controlIds, subscriber))
- binderDied()
- }
- }
+ fun maybeBindAndSubscribe(controlIds: List<String>, subscriber: IControlsSubscriber) =
+ invokeOrQueue(Subscribe(controlIds, subscriber))
/**
* Request a call to [ControlsProviderService.performControlAction].
@@ -266,19 +233,8 @@
* @param controlId the id of the [Control] the action is performed on
* @param action the action performed
*/
- fun maybeBindAndSendAction(controlId: String, action: ControlAction) {
- invokeOrQueue({ action(controlId, action) }, Message.Action(controlId, action))
- }
-
- private fun action(controlId: String, action: ControlAction) {
- if (DEBUG) {
- Log.d(TAG, "onAction $componentName - $controlId")
- }
- if (!(wrapper?.action(controlId, action, actionCallbackService) ?: false)) {
- queueMessage(Message.Action(controlId, action))
- binderDied()
- }
- }
+ fun maybeBindAndSendAction(controlId: String, action: ControlAction) =
+ invokeOrQueue(Action(controlId, action))
/**
* Starts the subscription to the [ControlsProviderService] and requests status of controls.
@@ -286,14 +242,14 @@
* @param subscription the subscription to use to request controls
* @see maybeBindAndLoad
*/
- fun startSubscription(subscription: IControlsSubscription) {
+ fun startSubscription(subscription: IControlsSubscription, requestLimit: Long) {
if (DEBUG) {
Log.d(TAG, "startSubscription: $subscription")
}
synchronized(subscriptions) {
subscriptions.add(subscription)
}
- wrapper?.request(subscription, MAX_CONTROLS_REQUEST)
+ wrapper?.request(subscription, requestLimit)
}
/**
@@ -316,7 +272,6 @@
* Request bind to the service.
*/
fun bindService() {
- unqueueMessageType(MSG_UNBIND)
bindService(true)
}
@@ -350,21 +305,55 @@
}
/**
- * Messages for the internal queue.
+ * Service methods that can be queued or invoked, and are retryable for failure scenarios
*/
- sealed class Message {
- abstract val type: Int
- class Load(val subscriber: IControlsSubscriber.Stub) : Message() {
- override val type = MSG_LOAD
+ abstract inner class ServiceMethod {
+ fun run() {
+ if (!callWrapper()) {
+ queueServiceMethod(this)
+ binderDied()
+ }
}
- object Unbind : Message() {
- override val type = MSG_UNBIND
+
+ internal abstract fun callWrapper(): Boolean
+ }
+
+ inner class Load(val subscriber: IControlsSubscriber.Stub) : ServiceMethod() {
+ override fun callWrapper(): Boolean {
+ if (DEBUG) {
+ Log.d(TAG, "load $componentName")
+ }
+ return wrapper?.load(subscriber) ?: false
}
- class Subscribe(val list: List<String>, val subscriber: IControlsSubscriber) : Message() {
- override val type = MSG_SUBSCRIBE
+ }
+
+ inner class Suggest(val subscriber: IControlsSubscriber.Stub) : ServiceMethod() {
+ override fun callWrapper(): Boolean {
+ if (DEBUG) {
+ Log.d(TAG, "suggest $componentName")
+ }
+ return wrapper?.loadSuggested(subscriber) ?: false
}
- class Action(val id: String, val action: ControlAction) : Message() {
- override val type = MSG_ACTION
+ }
+ inner class Subscribe(
+ val list: List<String>,
+ val subscriber: IControlsSubscriber
+ ) : ServiceMethod() {
+ override fun callWrapper(): Boolean {
+ if (DEBUG) {
+ Log.d(TAG, "subscribe $componentName - $list")
+ }
+
+ return wrapper?.subscribe(list, subscriber) ?: false
+ }
+ }
+
+ inner class Action(val id: String, val action: ControlAction) : ServiceMethod() {
+ override fun callWrapper(): Boolean {
+ if (DEBUG) {
+ Log.d(TAG, "onAction $componentName - $id")
+ }
+ return wrapper?.action(id, action, actionCallbackService) ?: false
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ServiceWrapper.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ServiceWrapper.kt
index b2afd3c..2c717f5 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ServiceWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ServiceWrapper.kt
@@ -50,6 +50,12 @@
}
}
+ fun loadSuggested(subscriber: IControlsSubscriber): Boolean {
+ return callThroughService {
+ service.loadSuggested(subscriber)
+ }
+ }
+
fun subscribe(controlIds: List<String>, subscriber: IControlsSubscriber): Boolean {
return callThroughService {
service.subscribe(controlIds, subscriber)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/StatefulControlSubscriber.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/StatefulControlSubscriber.kt
index a371aa6..e3eabff 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/StatefulControlSubscriber.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/StatefulControlSubscriber.kt
@@ -31,7 +31,8 @@
class StatefulControlSubscriber(
private val controller: ControlsController,
private val provider: ControlsProviderLifecycleManager,
- private val bgExecutor: DelayableExecutor
+ private val bgExecutor: DelayableExecutor,
+ private val requestLimit: Long
) : IControlsSubscriber.Stub() {
private var subscriptionOpen = false
private var subscription: IControlsSubscription? = null
@@ -50,7 +51,7 @@
run(token) {
subscriptionOpen = true
subscription = subs
- provider.startSubscription(subs)
+ provider.startSubscription(subs, requestLimit)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsRequestDialog.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsRequestDialog.kt
index a7fc2ac8..74a6c87 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsRequestDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsRequestDialog.kt
@@ -55,7 +55,7 @@
private lateinit var control: Control
private var dialog: Dialog? = null
private val callback = object : ControlsListingController.ControlsListingCallback {
- override fun onServicesUpdated(candidates: List<ControlsServiceInfo>) {}
+ override fun onServicesUpdated(serviceInfos: List<ControlsServiceInfo>) {}
}
private val currentUserTracker = object : CurrentUserTracker(broadcastDispatcher) {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index 138cd47..ffae4653 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -52,6 +52,7 @@
import dagger.Lazy
import java.text.Collator
+import java.util.function.Consumer
import javax.inject.Inject
import javax.inject.Singleton
@@ -89,6 +90,7 @@
private var popup: ListPopupWindow? = null
private var activeDialog: Dialog? = null
private val addControlsItem: SelectionItem
+ private var hidden = true
init {
val addDrawable = context.getDrawable(R.drawable.ic_add).apply {
@@ -134,11 +136,15 @@
override fun show(parent: ViewGroup) {
Log.d(ControlsUiController.TAG, "show()")
this.parent = parent
+ hidden = false
allStructures = controlsController.get().getFavorites()
selectedStructure = loadPreference(allStructures)
- if (selectedStructure.controls.isEmpty() && allStructures.size <= 1) {
+ val cb = Consumer<Boolean> { _ -> reload(parent) }
+ if (controlsController.get().addSeedingFavoritesCallback(cb)) {
+ listingCallback = createCallback(::showSeedingView)
+ } else if (selectedStructure.controls.isEmpty() && allStructures.size <= 1) {
// only show initial view if there are really no favorites across any structure
listingCallback = createCallback(::showInitialSetupView)
} else {
@@ -154,6 +160,20 @@
controlsListingController.get().addCallback(listingCallback)
}
+ private fun reload(parent: ViewGroup) {
+ if (hidden) return
+ show(parent)
+ }
+
+ private fun showSeedingView(items: List<SelectionItem>) {
+ parent.removeAllViews()
+
+ val inflater = LayoutInflater.from(context)
+ inflater.inflate(R.layout.controls_no_favorites, parent, true)
+ val subtitle = parent.requireViewById<TextView>(R.id.controls_subtitle)
+ subtitle.setVisibility(View.VISIBLE)
+ }
+
private fun showInitialSetupView(items: List<SelectionItem>) {
parent.removeAllViews()
@@ -320,13 +340,14 @@
selectedStructure = newSelection
updatePreferences(selectedStructure)
controlsListingController.get().removeCallback(listingCallback)
- show(parent)
+ reload(parent)
}
}
}
override fun hide() {
Log.d(ControlsUiController.TAG, "hide()")
+ hidden = true
popup?.dismiss()
activeDialog?.dismiss()
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index b99d765..73539f9 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -31,11 +31,13 @@
import android.app.admin.DevicePolicyManager;
import android.app.trust.TrustManager;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.SharedPreferences;
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.database.ContentObserver;
@@ -92,6 +94,8 @@
import com.android.systemui.MultiListLayout.MultiListAdapter;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.controls.ControlsServiceInfo;
+import com.android.systemui.controls.controller.ControlsController;
import com.android.systemui.controls.management.ControlsListingController;
import com.android.systemui.controls.ui.ControlsUiController;
import com.android.systemui.dagger.qualifiers.Background;
@@ -148,6 +152,9 @@
private static final String GLOBAL_ACTION_KEY_EMERGENCY = "emergency";
private static final String GLOBAL_ACTION_KEY_SCREENSHOT = "screenshot";
+ private static final String PREFS_CONTROLS_SEEDING_COMPLETED = "ControlsSeedingCompleted";
+ private static final String PREFS_CONTROLS_FILE = "controls_prefs";
+
private final Context mContext;
private final GlobalActionsManager mWindowManagerFuncs;
private final AudioManager mAudioManager;
@@ -215,7 +222,8 @@
NotificationShadeWindowController notificationShadeWindowController,
ControlsUiController controlsUiController, IWindowManager iWindowManager,
@Background Executor backgroundExecutor,
- ControlsListingController controlsListingController) {
+ ControlsListingController controlsListingController,
+ ControlsController controlsController) {
mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme);
mWindowManagerFuncs = windowManagerFuncs;
mAudioManager = audioManager;
@@ -279,9 +287,46 @@
}
});
- mControlsListingController.addCallback(list -> mAnyControlsProviders = !list.isEmpty());
+ String preferredControlsPackage = mContext.getResources()
+ .getString(com.android.systemui.R.string.config_controlsPreferredPackage);
+ mControlsListingController.addCallback(list -> {
+ mAnyControlsProviders = !list.isEmpty();
+
+ /*
+ * See if any service providers match the preferred component. If they do,
+ * and there are no current favorites, and we haven't successfully loaded favorites to
+ * date, query the preferred component for a limited number of suggested controls.
+ */
+ ComponentName preferredComponent = null;
+ for (ControlsServiceInfo info : list) {
+ if (info.componentName.getPackageName().equals(preferredControlsPackage)) {
+ preferredComponent = info.componentName;
+ break;
+ }
+ }
+
+ if (preferredComponent == null) return;
+
+ SharedPreferences prefs = context.getSharedPreferences(PREFS_CONTROLS_FILE,
+ Context.MODE_PRIVATE);
+ boolean isSeeded = prefs.getBoolean(PREFS_CONTROLS_SEEDING_COMPLETED, false);
+ boolean hasFavorites = controlsController.getFavorites().size() > 0;
+ if (!isSeeded && !hasFavorites) {
+ controlsController.seedFavoritesForComponent(
+ preferredComponent,
+ (accepted) -> {
+ Log.i(TAG, "Controls seeded: " + accepted);
+ prefs.edit().putBoolean(PREFS_CONTROLS_SEEDING_COMPLETED,
+ accepted).apply();
+ }
+ );
+ }
+ });
}
+
+
+
/**
* Show the global actions dialog (creating if necessary)
*
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
index 42a7c6a..f6f8363 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
@@ -447,7 +447,6 @@
}
}
-
@VisibleForTesting
ExpandableView getGentleHeaderView() {
return mGentleHeader;
@@ -471,7 +470,7 @@
private final ConfigurationListener mConfigurationListener = new ConfigurationListener() {
@Override
public void onLocaleListChanged() {
- mGentleHeader.reinflateContents();
+ reinflateViews(LayoutInflater.from(mParent.getContext()));
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt
index 1b4f98f..bc25c71 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt
@@ -76,9 +76,7 @@
}
}
- override fun needsClippingToShelf(): Boolean {
- return true
- }
+ override fun needsClippingToShelf(): Boolean = true
override fun applyContentTransformation(contentAlpha: Float, translationY: Float) {
super.applyContentTransformation(contentAlpha, translationY)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java
index deb5532..a3d8eec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java
@@ -20,7 +20,6 @@
import android.annotation.StringRes;
import android.content.Context;
import android.util.AttributeSet;
-import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
@@ -30,16 +29,17 @@
import com.android.systemui.R;
import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
-import java.util.Objects;
-
/**
- * Similar in size and appearance to the NotificationShelf, appears at the beginning of some
- * notification sections. Currently only used for gentle notifications.
+ * Header displayed above a notification section in the shade. Currently used for Alerting and
+ * Silent sections.
*/
public class SectionHeaderView extends StackScrollerDecorView {
+
private ViewGroup mContents;
private TextView mLabelView;
private ImageView mClearAllButton;
+ @StringRes @Nullable private Integer mLabelTextId;
+ @Nullable private View.OnClickListener mLabelClickListener = null;
@Nullable private View.OnClickListener mOnClearClickListener = null;
public SectionHeaderView(Context context, AttributeSet attrs) {
@@ -48,18 +48,24 @@
@Override
protected void onFinishInflate() {
- mContents = Objects.requireNonNull(findViewById(R.id.content));
+ mContents = requireViewById(R.id.content);
bindContents();
super.onFinishInflate();
setVisible(true /* nowVisible */, false /* animate */);
}
private void bindContents() {
- mLabelView = Objects.requireNonNull(findViewById(R.id.header_label));
- mClearAllButton = Objects.requireNonNull(findViewById(R.id.btn_clear_all));
+ mLabelView = requireViewById(R.id.header_label);
+ mClearAllButton = requireViewById(R.id.btn_clear_all);
if (mOnClearClickListener != null) {
mClearAllButton.setOnClickListener(mOnClearClickListener);
}
+ if (mLabelClickListener != null) {
+ mLabelView.setOnClickListener(mLabelClickListener);
+ }
+ if (mLabelTextId != null) {
+ mLabelView.setText(mLabelTextId);
+ }
}
@Override
@@ -72,21 +78,6 @@
return null;
}
- /**
- * Destroys and reinflates the visible contents of the section header. For use on configuration
- * changes or any other time that layout values might need to be re-evaluated.
- *
- * Does not reinflate the base content view itself ({@link #findContentView()} or any of the
- * decorator views, such as the background view or shadow view.
- */
- void reinflateContents() {
- mContents.removeAllViews();
- LayoutInflater.from(getContext()).inflate(
- R.layout.status_bar_notification_section_header_contents,
- mContents);
- bindContents();
- }
-
@Override
public boolean isTransparent() {
return true;
@@ -105,6 +96,7 @@
* Fired whenever the user clicks on the body of the header (e.g. no sub-buttons or anything).
*/
void setOnHeaderClickListener(View.OnClickListener listener) {
+ mLabelClickListener = listener;
mLabelView.setOnClickListener(listener);
}
@@ -129,6 +121,7 @@
}
void setHeaderText(@StringRes int resId) {
+ mLabelTextId = resId;
mLabelView.setText(resId);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt
index c25d4e2..2ff2b2a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt
@@ -207,6 +207,56 @@
}
@Test
+ fun testBindAndLoadSuggested() {
+ val callback = object : ControlsBindingController.LoadCallback {
+ override fun error(message: String) {}
+
+ override fun accept(t: List<Control>) {}
+ }
+ controller.bindAndLoadSuggested(TEST_COMPONENT_NAME_1, callback)
+
+ verify(providers[0]).maybeBindAndLoadSuggested(any())
+ }
+
+ @Test
+ fun testLoadSuggested_onCompleteRemovesTimeout() {
+ val callback = object : ControlsBindingController.LoadCallback {
+ override fun error(message: String) {}
+
+ override fun accept(t: List<Control>) {}
+ }
+ val subscription = mock(IControlsSubscription::class.java)
+
+ controller.bindAndLoadSuggested(TEST_COMPONENT_NAME_1, callback)
+
+ verify(providers[0]).maybeBindAndLoadSuggested(capture(subscriberCaptor))
+ val b = Binder()
+ subscriberCaptor.value.onSubscribe(b, subscription)
+
+ subscriberCaptor.value.onComplete(b)
+ verify(providers[0]).cancelLoadTimeout()
+ }
+
+ @Test
+ fun testLoadSuggested_onErrorRemovesTimeout() {
+ val callback = object : ControlsBindingController.LoadCallback {
+ override fun error(message: String) {}
+
+ override fun accept(t: List<Control>) {}
+ }
+ val subscription = mock(IControlsSubscription::class.java)
+
+ controller.bindAndLoadSuggested(TEST_COMPONENT_NAME_1, callback)
+
+ verify(providers[0]).maybeBindAndLoadSuggested(capture(subscriberCaptor))
+ val b = Binder()
+ subscriberCaptor.value.onSubscribe(b, subscription)
+
+ subscriberCaptor.value.onError(b, "")
+ verify(providers[0]).cancelLoadTimeout()
+ }
+
+ @Test
fun testBindService() {
controller.bindService(TEST_COMPONENT_NAME_1)
executor.runAllReady()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
index f9c9815..971d14c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
@@ -80,6 +80,10 @@
@Captor
private lateinit var structureInfoCaptor: ArgumentCaptor<StructureInfo>
+
+ @Captor
+ private lateinit var booleanConsumer: ArgumentCaptor<Consumer<Boolean>>
+
@Captor
private lateinit var controlLoadCallbackCaptor:
ArgumentCaptor<ControlsBindingController.LoadCallback>
@@ -155,9 +159,13 @@
verify(listingController).addCallback(capture(listingCallbackCaptor))
}
- private fun builderFromInfo(controlInfo: ControlInfo): Control.StatelessBuilder {
+ private fun builderFromInfo(
+ controlInfo: ControlInfo,
+ structure: CharSequence = ""
+ ): Control.StatelessBuilder {
return Control.StatelessBuilder(controlInfo.controlId, pendingIntent)
.setDeviceType(controlInfo.deviceType).setTitle(controlInfo.controlTitle)
+ .setStructure(structure)
}
@Test
@@ -746,4 +754,70 @@
inOrder.verify(persistenceWrapper).readFavorites()
inOrder.verify(listingController).addCallback(listingCallbackCaptor.value)
}
+
+ @Test
+ fun testSeedFavoritesForComponent() {
+ var succeeded = false
+ val control = builderFromInfo(TEST_CONTROL_INFO, TEST_STRUCTURE_INFO.structure).build()
+
+ controller.seedFavoritesForComponent(TEST_COMPONENT, Consumer { accepted ->
+ succeeded = accepted
+ })
+
+ verify(bindingController).bindAndLoadSuggested(eq(TEST_COMPONENT),
+ capture(controlLoadCallbackCaptor))
+
+ controlLoadCallbackCaptor.value.accept(listOf(control))
+
+ delayableExecutor.runAllReady()
+
+ assertEquals(listOf(TEST_STRUCTURE_INFO),
+ controller.getFavoritesForComponent(TEST_COMPONENT))
+ assertTrue(succeeded)
+ }
+
+ @Test
+ fun testSeedFavoritesForComponent_error() {
+ var succeeded = false
+
+ controller.seedFavoritesForComponent(TEST_COMPONENT, Consumer { accepted ->
+ succeeded = accepted
+ })
+
+ verify(bindingController).bindAndLoadSuggested(eq(TEST_COMPONENT),
+ capture(controlLoadCallbackCaptor))
+
+ controlLoadCallbackCaptor.value.error("Error loading")
+
+ delayableExecutor.runAllReady()
+
+ assertEquals(listOf<StructureInfo>(), controller.getFavoritesForComponent(TEST_COMPONENT))
+ assertFalse(succeeded)
+ }
+
+ @Test
+ fun testSeedFavoritesForComponent_inProgressCallback() {
+ var succeeded = false
+ var seeded = false
+ val control = builderFromInfo(TEST_CONTROL_INFO, TEST_STRUCTURE_INFO.structure).build()
+
+ controller.seedFavoritesForComponent(TEST_COMPONENT, Consumer { accepted ->
+ succeeded = accepted
+ })
+
+ verify(bindingController).bindAndLoadSuggested(eq(TEST_COMPONENT),
+ capture(controlLoadCallbackCaptor))
+
+ controller.addSeedingFavoritesCallback(Consumer { accepted ->
+ seeded = accepted
+ })
+ controlLoadCallbackCaptor.value.accept(listOf(control))
+
+ delayableExecutor.runAllReady()
+
+ assertEquals(listOf(TEST_STRUCTURE_INFO),
+ controller.getFavoritesForComponent(TEST_COMPONENT))
+ assertTrue(succeeded)
+ assertTrue(seeded)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ServiceWrapperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ServiceWrapperTest.kt
index cd82844..789d6df 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ServiceWrapperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ServiceWrapperTest.kt
@@ -92,6 +92,22 @@
}
@Test
+ fun testLoadSuggested_happyPath() {
+ val result = wrapper.loadSuggested(subscriber)
+
+ assertTrue(result)
+ verify(service).loadSuggested(subscriber)
+ }
+
+ @Test
+ fun testLoadSuggested_error() {
+ `when`(service.loadSuggested(any())).thenThrow(exception)
+ val result = wrapper.loadSuggested(subscriber)
+
+ assertFalse(result)
+ }
+
+ @Test
fun testSubscribe_happyPath() {
val list = listOf("TEST_ID")
val result = wrapper.subscribe(list, subscriber)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/StatefulControlSubscriberTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/StatefulControlSubscriberTest.kt
index ff5c8d4..267520e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/StatefulControlSubscriberTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/StatefulControlSubscriberTest.kt
@@ -59,13 +59,15 @@
private lateinit var scs: StatefulControlSubscriber
+ private val REQUEST_LIMIT = 5L
+
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
`when`(provider.componentName).thenReturn(TEST_COMPONENT)
`when`(provider.token).thenReturn(token)
- scs = StatefulControlSubscriber(controller, provider, executor)
+ scs = StatefulControlSubscriber(controller, provider, executor, REQUEST_LIMIT)
}
@Test
@@ -73,7 +75,7 @@
scs.onSubscribe(token, subscription)
executor.runAllReady()
- verify(provider).startSubscription(subscription)
+ verify(provider).startSubscription(subscription, REQUEST_LIMIT)
}
@Test
@@ -81,7 +83,7 @@
scs.onSubscribe(badToken, subscription)
executor.runAllReady()
- verify(provider, never()).startSubscription(subscription)
+ verify(provider, never()).startSubscription(subscription, REQUEST_LIMIT)
}
@Test
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
index 25585b3..7047a9e 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
@@ -100,6 +100,7 @@
@NonNull IResourcesReclaimListener listener, @NonNull int[] clientId)
throws RemoteException {
enforceTrmAccessPermission("registerClientProfile");
+ enforceTunerAccessPermission("registerClientProfile");
if (profile == null) {
throw new RemoteException("ResourceClientProfile can't be null");
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index eb18678..83fff28 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -186,6 +186,7 @@
import android.os.Trace;
import android.os.UserHandle;
import android.os.WorkSource;
+import android.provider.DeviceConfig;
import android.provider.Settings;
import android.service.vr.IVrManager;
import android.service.vr.IVrStateCallbacks;
@@ -1167,7 +1168,9 @@
mAnimator = new WindowAnimator(this);
mRoot = new RootWindowContainer(this);
- mUseBLAST = true;
+ mUseBLAST = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_WINDOW_MANAGER_NATIVE_BOOT,
+ WM_USE_BLAST_ADAPTER_FLAG, false);
mWindowPlacerLocked = new WindowSurfacePlacer(this);
mTaskSnapshotController = new TaskSnapshotController(this);
diff --git a/telephony/api/system-current.txt b/telephony/api/system-current.txt
index 092cb7f..11568f1 100644
--- a/telephony/api/system-current.txt
+++ b/telephony/api/system-current.txt
@@ -882,9 +882,6 @@
field public static final String ACTION_SIM_APPLICATION_STATE_CHANGED = "android.telephony.action.SIM_APPLICATION_STATE_CHANGED";
field public static final String ACTION_SIM_CARD_STATE_CHANGED = "android.telephony.action.SIM_CARD_STATE_CHANGED";
field public static final String ACTION_SIM_SLOT_STATUS_CHANGED = "android.telephony.action.SIM_SLOT_STATUS_CHANGED";
- field public static final int CARD_POWER_DOWN = 0; // 0x0
- field public static final int CARD_POWER_UP = 1; // 0x1
- field public static final int CARD_POWER_UP_PASS_THROUGH = 2; // 0x2
field public static final int CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES = -2; // 0xfffffffe
field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1
field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 979554d..d6f5bb2 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1036,7 +1036,9 @@
"android.telephony.extra.VOICEMAIL_SCRAMBLED_PIN_STRING";
/**
- * Broadcast intent that indicates multi-SIM configuration is changed. For example, it changed
+ * Broadcast action to be received by Broadcast receivers.
+ *
+ * Indicates multi-SIM configuration is changed. For example, it changed
* from single SIM capable to dual-SIM capable (DSDS or DSDA) or triple-SIM mode.
*
* It doesn't indicate how many subscriptions are actually active, or which states SIMs are,
@@ -9741,14 +9743,12 @@
* Powers down the SIM. SIM must be up prior.
* @hide
*/
- @SystemApi
public static final int CARD_POWER_DOWN = 0;
/**
* Powers up the SIM normally. SIM must be down prior.
* @hide
*/
- @SystemApi
public static final int CARD_POWER_UP = 1;
/**
@@ -9766,7 +9766,6 @@
* is NOT persistent across boots. On reboot, SIM will power up normally.
* @hide
*/
- @SystemApi
public static final int CARD_POWER_UP_PASS_THROUGH = 2;
/**