Merge changes from topics "wifi_oem_migration", "wifi_scan_throttle_api"
* changes:
WifiOemMigrationHook: Rename class & methods to be more generic
WifiManager: Add API for scan throttling
diff --git a/Android.bp b/Android.bp
index 12bc906..69d654f 100644
--- a/Android.bp
+++ b/Android.bp
@@ -403,7 +403,6 @@
"unsupportedappusage",
"framework-media-stubs-systemapi",
"framework-mediaprovider-stubs-systemapi",
- "framework-tethering",
"framework-telephony-stubs",
],
@@ -472,6 +471,7 @@
"framework-permission-stubs-systemapi",
"framework-wifi-stubs",
"ike-stubs",
+ "framework-tethering-stubs",
],
installable: true,
javac_shard_size: 150,
@@ -496,6 +496,7 @@
"//frameworks/base/apex/blobstore/framework",
"//frameworks/base/apex/jobscheduler/framework",
"//frameworks/base/apex/statsd/service",
+ "//frameworks/base/packages/Tethering/tests/unit",
],
}
@@ -523,8 +524,7 @@
"framework-statsd",
"framework-wifi-stubs",
"ike-stubs",
- // TODO(b/147200698): should be the stub of framework-tethering
- "framework-tethering",
+ "framework-tethering-stubs",
// TODO (b/147688669) should be framework-telephony-stubs
"framework-telephony",
// TODO(jiyong): add stubs for APEXes here
diff --git a/api/current.txt b/api/current.txt
index 640e85e..167c8c1 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -31768,7 +31768,7 @@
field public static final int GROUP_OWNER_INTENT_MAX = 15; // 0xf
field public static final int GROUP_OWNER_INTENT_MIN = 0; // 0x0
field public String deviceAddress;
- field public int groupOwnerIntent;
+ field @IntRange(from=0, to=15) public int groupOwnerIntent;
field public android.net.wifi.WpsInfo wps;
}
@@ -31960,14 +31960,14 @@
method public int getDeviceType();
method public int getMaxThroughput();
method public boolean isContentProtectionSupported();
+ method public boolean isEnabled();
method public boolean isSessionAvailable();
- method public boolean isWfdEnabled();
method public void setContentProtectionSupported(boolean);
- method public void setControlPort(int);
+ method public void setControlPort(@IntRange(from=0) int);
method public boolean setDeviceType(int);
- method public void setMaxThroughput(int);
+ method public void setEnabled(boolean);
+ method public void setMaxThroughput(@IntRange(from=0) int);
method public void setSessionAvailable(boolean);
- method public void setWfdEnabled(boolean);
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.p2p.WifiP2pWfdInfo> CREATOR;
field public static final int DEVICE_TYPE_PRIMARY_SINK = 1; // 0x1
diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt
index 90531b1..8100b07 100644
--- a/api/module-lib-current.txt
+++ b/api/module-lib-current.txt
@@ -1,4 +1,125 @@
// Signature format: 2.0
+package android.net {
+
+ public final class TetheredClient implements android.os.Parcelable {
+ ctor public TetheredClient(@NonNull android.net.MacAddress, @NonNull java.util.Collection<android.net.TetheredClient.AddressInfo>, int);
+ method public int describeContents();
+ method @NonNull public java.util.List<android.net.TetheredClient.AddressInfo> getAddresses();
+ method @NonNull public android.net.MacAddress getMacAddress();
+ method public int getTetheringType();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient> CREATOR;
+ }
+
+ public static final class TetheredClient.AddressInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public android.net.LinkAddress getAddress();
+ method @Nullable public String getHostname();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient.AddressInfo> CREATOR;
+ }
+
+ public class TetheringConstants {
+ ctor public TetheringConstants();
+ field public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType";
+ field public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback";
+ field public static final String EXTRA_REM_TETHER_TYPE = "extraRemTetherType";
+ field public static final String EXTRA_RUN_PROVISION = "extraRunProvision";
+ field public static final String EXTRA_SET_ALARM = "extraSetAlarm";
+ }
+
+ public class TetheringManager {
+ ctor public TetheringManager(@NonNull android.content.Context, @NonNull java.util.function.Supplier<android.os.IBinder>);
+ method public int getLastTetherError(@NonNull String);
+ method @NonNull public String[] getTetherableBluetoothRegexs();
+ method @NonNull public String[] getTetherableIfaces();
+ method @NonNull public String[] getTetherableUsbRegexs();
+ method @NonNull public String[] getTetherableWifiRegexs();
+ method @NonNull public String[] getTetheredIfaces();
+ method @NonNull public String[] getTetheringErroredIfaces();
+ method public boolean isTetheringSupported();
+ method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.TetheringEventCallback);
+ method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void requestLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.OnTetheringEntitlementResultListener);
+ method public void requestLatestTetheringEntitlementResult(int, @NonNull android.os.ResultReceiver, boolean);
+ method @Deprecated public int setUsbTethering(boolean);
+ method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(@NonNull android.net.TetheringManager.TetheringRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback);
+ method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(int, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback);
+ method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void stopAllTethering();
+ method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void stopTethering(int);
+ method @Deprecated public int tether(@NonNull String);
+ method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.ACCESS_NETWORK_STATE}) public void unregisterTetheringEventCallback(@NonNull android.net.TetheringManager.TetheringEventCallback);
+ method @Deprecated public int untether(@NonNull String);
+ field public static final String ACTION_TETHER_STATE_CHANGED = "android.net.conn.TETHER_STATE_CHANGED";
+ field public static final String EXTRA_ACTIVE_LOCAL_ONLY = "android.net.extra.ACTIVE_LOCAL_ONLY";
+ field public static final String EXTRA_ACTIVE_TETHER = "tetherArray";
+ field public static final String EXTRA_AVAILABLE_TETHER = "availableArray";
+ field public static final String EXTRA_ERRORED_TETHER = "erroredArray";
+ field public static final int TETHERING_BLUETOOTH = 2; // 0x2
+ field public static final int TETHERING_ETHERNET = 5; // 0x5
+ field public static final int TETHERING_INVALID = -1; // 0xffffffff
+ field public static final int TETHERING_NCM = 4; // 0x4
+ field public static final int TETHERING_USB = 1; // 0x1
+ field public static final int TETHERING_WIFI = 0; // 0x0
+ field public static final int TETHERING_WIFI_P2P = 3; // 0x3
+ field public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12; // 0xc
+ field public static final int TETHER_ERROR_DISABLE_NAT_ERROR = 9; // 0x9
+ field public static final int TETHER_ERROR_ENABLE_NAT_ERROR = 8; // 0x8
+ field public static final int TETHER_ERROR_ENTITLEMENT_UNKNOWN = 13; // 0xd
+ field public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10; // 0xa
+ field public static final int TETHER_ERROR_MASTER_ERROR = 5; // 0x5
+ field public static final int TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION = 15; // 0xf
+ field public static final int TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION = 14; // 0xe
+ field public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0
+ field public static final int TETHER_ERROR_PROVISION_FAILED = 11; // 0xb
+ field public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2; // 0x2
+ field public static final int TETHER_ERROR_TETHER_IFACE_ERROR = 6; // 0x6
+ field public static final int TETHER_ERROR_UNAVAIL_IFACE = 4; // 0x4
+ field public static final int TETHER_ERROR_UNKNOWN_IFACE = 1; // 0x1
+ field public static final int TETHER_ERROR_UNSUPPORTED = 3; // 0x3
+ field public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7; // 0x7
+ }
+
+ public static interface TetheringManager.OnTetheringEntitlementResultListener {
+ method public void onTetheringEntitlementResult(int);
+ }
+
+ public abstract static class TetheringManager.StartTetheringCallback {
+ ctor public TetheringManager.StartTetheringCallback();
+ method public void onTetheringFailed(int);
+ method public void onTetheringStarted();
+ }
+
+ public abstract static class TetheringManager.TetheringEventCallback {
+ ctor public TetheringManager.TetheringEventCallback();
+ method public void onClientsChanged(@NonNull java.util.Collection<android.net.TetheredClient>);
+ method public void onError(@NonNull String, int);
+ method @Deprecated public void onTetherableInterfaceRegexpsChanged(@NonNull android.net.TetheringManager.TetheringInterfaceRegexps);
+ method public void onTetherableInterfacesChanged(@NonNull java.util.List<java.lang.String>);
+ method public void onTetheredInterfacesChanged(@NonNull java.util.List<java.lang.String>);
+ method public void onTetheringSupported(boolean);
+ method public void onUpstreamChanged(@Nullable android.net.Network);
+ }
+
+ @Deprecated public static class TetheringManager.TetheringInterfaceRegexps {
+ ctor @Deprecated public TetheringManager.TetheringInterfaceRegexps(@NonNull String[], @NonNull String[], @NonNull String[]);
+ method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableBluetoothRegexs();
+ method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableUsbRegexs();
+ method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableWifiRegexs();
+ }
+
+ public static class TetheringManager.TetheringRequest {
+ }
+
+ public static class TetheringManager.TetheringRequest.Builder {
+ ctor public TetheringManager.TetheringRequest.Builder(int);
+ method @NonNull public android.net.TetheringManager.TetheringRequest build();
+ method @NonNull @RequiresPermission("android.permission.TETHER_PRIVILEGED") public android.net.TetheringManager.TetheringRequest.Builder setExemptFromEntitlementCheck(boolean);
+ method @NonNull @RequiresPermission("android.permission.TETHER_PRIVILEGED") public android.net.TetheringManager.TetheringRequest.Builder setSilentProvisioning(boolean);
+ method @NonNull @RequiresPermission("android.permission.TETHER_PRIVILEGED") public android.net.TetheringManager.TetheringRequest.Builder useStaticIpv4Addresses(@NonNull android.net.LinkAddress);
+ }
+
+}
+
package android.util {
public final class Log {
diff --git a/api/system-current.txt b/api/system-current.txt
index 6071c1a..6b3067a 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -8112,7 +8112,7 @@
public final class WifiP2pGroupList implements android.os.Parcelable {
method public int describeContents();
- method @NonNull public java.util.Collection<android.net.wifi.p2p.WifiP2pGroup> getGroupList();
+ method @NonNull public java.util.List<android.net.wifi.p2p.WifiP2pGroup> getGroupList();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.p2p.WifiP2pGroupList> CREATOR;
}
@@ -8120,12 +8120,13 @@
public class WifiP2pManager {
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.OVERRIDE_WIFI_CONFIG}) public void deletePersistentGroup(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, int, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener);
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void factoryReset(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener);
- method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void listen(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, boolean, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener);
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.READ_WIFI_CREDENTIAL}) public void requestPersistentGroupInfo(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @Nullable android.net.wifi.p2p.WifiP2pManager.PersistentGroupInfoListener);
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.OVERRIDE_WIFI_CONFIG}) public void setDeviceName(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @NonNull String, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener);
method @RequiresPermission(allOf={android.Manifest.permission.CONNECTIVITY_INTERNAL, android.Manifest.permission.CONFIGURE_WIFI_DISPLAY}) public void setMiracastMode(int);
method @RequiresPermission(android.Manifest.permission.CONFIGURE_WIFI_DISPLAY) public void setWfdInfo(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @NonNull android.net.wifi.p2p.WifiP2pWfdInfo, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener);
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.OVERRIDE_WIFI_CONFIG}) public void setWifiP2pChannels(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, int, int, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener);
+ method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void startListening(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener);
+ method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void stopListening(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener);
field public static final String ACTION_WIFI_P2P_PERSISTENT_GROUPS_CHANGED = "android.net.wifi.p2p.action.WIFI_P2P_PERSISTENT_GROUPS_CHANGED";
field public static final int MIRACAST_DISABLED = 0; // 0x0
field public static final int MIRACAST_SINK = 2; // 0x2
diff --git a/core/java/android/app/WindowContext.java b/core/java/android/app/WindowContext.java
index 36ae450..d279983 100644
--- a/core/java/android/app/WindowContext.java
+++ b/core/java/android/app/WindowContext.java
@@ -60,12 +60,12 @@
if (token != null && !isWindowToken(token)) {
throw new IllegalArgumentException("Token must be registered to server.");
}
+ mToken = token != null ? token : new Binder();
- final ContextImpl contextImpl = createBaseWindowContext(base, token);
+ final ContextImpl contextImpl = createBaseWindowContext(base, mToken);
attachBaseContext(contextImpl);
contextImpl.setOuterContext(this);
- mToken = token != null ? token : new Binder();
mDisplayId = getDisplayId();
mWindowManager = new WindowManagerImpl(this);
mWindowManager.setDefaultToken(mToken);
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index f644f14..a305948 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -2423,14 +2423,14 @@
/**
* Get the set of tethered dhcp ranges.
*
- * @return an array of 0 or more {@code String} of tethered dhcp ranges.
- * @deprecated This API just return the default value which is not used in DhcpServer.
+ * @deprecated This method is not supported.
+ * TODO: remove this function when all of clients are removed.
* {@hide}
*/
@RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
@Deprecated
public String[] getTetheredDhcpRanges() {
- return getTetheringManager().getTetheredDhcpRanges();
+ throw new UnsupportedOperationException("getTetheredDhcpRanges is not supported");
}
/**
diff --git a/core/java/android/util/apk/ApkSignatureVerifier.java b/core/java/android/util/apk/ApkSignatureVerifier.java
index b933664..1d3e6d0 100644
--- a/core/java/android/util/apk/ApkSignatureVerifier.java
+++ b/core/java/android/util/apk/ApkSignatureVerifier.java
@@ -74,8 +74,7 @@
* This method is dangerous and should not be used, unless the caller is absolutely certain the
* APK is trusted.
*
- * @throws PackageParserException if the APK's signature failed to verify.
- * or greater is not found, except in the case of no JAR signature.
+ * @throws PackageParserException if there was a problem collecting certificates.
*/
public static PackageParser.SigningDetails unsafeGetCertsWithoutVerification(
String apkPath, int minSignatureSchemeVersion)
@@ -147,7 +146,7 @@
*
* @param verifyFull whether to verify all contents of this APK or just collect certificates.
* @return the certificates associated with each signer.
- * @throws SignatureNotFoundException is there are no V3 signatures in the APK
+ * @throws SignatureNotFoundException if there are no V3 signatures in the APK
* @throws PackageParserException if there was a problem collecting certificates
*/
private static PackageParser.SigningDetails verifyV3Signature(String apkPath,
@@ -188,7 +187,7 @@
*
* @param verifyFull whether to verify all contents of this APK or just collect certificates.
* @return the certificates associated with each signer.
- * @throws SignatureNotFoundException is there are no V2 signatures in the APK
+ * @throws SignatureNotFoundException if there are no V2 signatures in the APK
* @throws PackageParserException if there was a problem collecting certificates
*/
private static PackageParser.SigningDetails verifyV2Signature(String apkPath,
diff --git a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
index 82eb55a..5f35622 100644
--- a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
+++ b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
@@ -471,6 +471,7 @@
holder.mSwitchItem.setVisibility(View.GONE);
holder.mItemContainer.setVisibility(isLaunchMenuMode ? View.GONE : View.VISIBLE);
holder.mItemView.setEnabled(enabledState);
+ holder.mItemView.setClickable(!enabledState);
}
private void updateInvisibleActionItemVisibility(@NonNull Context context,
@@ -485,6 +486,7 @@
holder.mItemContainer.setVisibility((mShortcutMenuMode == ShortcutMenuMode.EDIT)
? View.VISIBLE : View.GONE);
holder.mItemView.setEnabled(true);
+ holder.mItemView.setClickable(false);
}
private void updateIntuitiveActionItemVisibility(@NonNull Context context,
@@ -504,6 +506,7 @@
holder.mSwitchItem.setChecked(!isEditMenuMode && isServiceEnabled);
holder.mItemContainer.setVisibility(View.VISIBLE);
holder.mItemView.setEnabled(true);
+ holder.mItemView.setClickable(false);
}
private void updateBounceActionItemVisibility(@NonNull Context context,
@@ -518,6 +521,7 @@
holder.mSwitchItem.setVisibility(View.GONE);
holder.mItemContainer.setVisibility(View.VISIBLE);
holder.mItemView.setEnabled(true);
+ holder.mItemView.setClickable(false);
}
}
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index fc0a2ef..c14d99c 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -2578,4 +2578,7 @@
// CATEGORY: SETTINGS
// OS: R
CONNECTION_DEVICE_ADVANCED_NFC = 1828;
+
+ // OPEN: Settings -> Apps & Notifications -> Special App Access
+ INTERACT_ACROSS_PROFILES = 1829;
}
diff --git a/core/res/res/layout/accessibility_button_chooser_item.xml b/core/res/res/layout/accessibility_button_chooser_item.xml
index d6fd7aa..d19e313 100644
--- a/core/res/res/layout/accessibility_button_chooser_item.xml
+++ b/core/res/res/layout/accessibility_button_chooser_item.xml
@@ -20,7 +20,6 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:clickable="true"
android:gravity="center"
android:paddingStart="16dp"
android:paddingEnd="16dp"
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index d7b74df..6418610 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -471,7 +471,8 @@
synchronized (sRouterLock) {
for (MediaRoute2Info route : routes) {
mRoutes.put(route.getId(), route);
- if (route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) {
+ if (route.isSystemRoute()
+ || route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) {
addedRoutes.add(route);
}
}
@@ -487,7 +488,8 @@
synchronized (sRouterLock) {
for (MediaRoute2Info route : routes) {
mRoutes.remove(route.getId());
- if (route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) {
+ if (route.isSystemRoute()
+ || route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) {
removedRoutes.add(route);
}
}
@@ -503,7 +505,8 @@
synchronized (sRouterLock) {
for (MediaRoute2Info route : routes) {
mRoutes.put(route.getId(), route);
- if (route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) {
+ if (route.isSystemRoute()
+ || route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) {
changedRoutes.add(route);
}
}
@@ -643,8 +646,8 @@
private List<MediaRoute2Info> filterRoutes(List<MediaRoute2Info> routes,
RouteDiscoveryPreference discoveryRequest) {
return routes.stream()
- .filter(
- route -> route.hasAnyFeatures(discoveryRequest.getPreferredFeatures()))
+ .filter(route -> route.isSystemRoute()
+ || route.hasAnyFeatures(discoveryRequest.getPreferredFeatures()))
.collect(Collectors.toList());
}
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index 38cf5ab..617305c 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -38,7 +38,6 @@
import android.provider.DocumentsContract.Document;
import android.provider.DocumentsContract.Path;
import android.provider.DocumentsContract.Root;
-import android.provider.MediaStore;
import android.provider.Settings;
import android.system.ErrnoException;
import android.system.Os;
@@ -100,7 +99,6 @@
private static final String ROOT_ID_PRIMARY_EMULATED =
DocumentsContract.EXTERNAL_STORAGE_PRIMARY_EMULATED_ROOT_ID;
- private static final String ROOT_ID_HOME = "home";
private static final String GET_DOCUMENT_URI_CALL = "get_document_uri";
private static final String GET_MEDIA_URI_CALL = "get_media_uri";
@@ -156,7 +154,6 @@
private void updateVolumesLocked() {
mRoots.clear();
- VolumeInfo primaryVolume = null;
final int userId = UserHandle.myUserId();
final List<VolumeInfo> volumes = mStorageManager.getVolumes();
for (VolumeInfo volume : volumes) {
@@ -234,8 +231,6 @@
}
if (volume.isPrimary()) {
- // save off the primary volume for subsequent "Home" dir initialization.
- primaryVolume = volume;
root.flags |= Root.FLAG_ADVANCED;
}
// Dunno when this would NOT be the case, but never hurts to be correct.
@@ -259,37 +254,6 @@
}
}
- // Finally, if primary storage is available we add the "Documents" directory.
- // If I recall correctly the actual directory is created on demand
- // by calling either getPathForUser, or getInternalPathForUser.
- if (primaryVolume != null && primaryVolume.isVisible()) {
- final RootInfo root = new RootInfo();
- root.rootId = ROOT_ID_HOME;
- mRoots.put(root.rootId, root);
- root.title = getContext().getString(R.string.root_documents);
-
- // Only report bytes on *volumes*...as a matter of policy.
- root.reportAvailableBytes = false;
- root.flags = Root.FLAG_LOCAL_ONLY | Root.FLAG_SUPPORTS_SEARCH
- | Root.FLAG_SUPPORTS_IS_CHILD;
-
- // Dunno when this would NOT be the case, but never hurts to be correct.
- if (primaryVolume.isMountedWritable()) {
- root.flags |= Root.FLAG_SUPPORTS_CREATE;
- }
-
- // Create the "Documents" directory on disk (don't use the localized title).
- root.visiblePath = new File(
- primaryVolume.getPathForUser(userId), Environment.DIRECTORY_DOCUMENTS);
- root.path = new File(
- primaryVolume.getInternalPathForUser(userId), Environment.DIRECTORY_DOCUMENTS);
- try {
- root.docId = getDocIdForFile(root.path);
- } catch (FileNotFoundException e) {
- throw new IllegalStateException(e);
- }
- }
-
Log.d(TAG, "After updating volumes, found " + mRoots.size() + " active roots");
// Note this affects content://com.android.externalstorage.documents/root/39BD-07C5
diff --git a/packages/SettingsLib/LayoutPreference/res/drawable/ic_swap_horiz_blue.xml b/packages/SettingsLib/LayoutPreference/res/drawable/ic_swap_horiz_blue.xml
new file mode 100644
index 0000000..04de174
--- /dev/null
+++ b/packages/SettingsLib/LayoutPreference/res/drawable/ic_swap_horiz_blue.xml
@@ -0,0 +1,25 @@
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M6.99,11L3,15l3.99,4v-3H14v-2H6.99v-3zM21,9l-3.99,-4v3H10v2h7.01v3L21,9z"
+ android:fillColor="#4285F4"/>
+</vector>
diff --git a/packages/SettingsLib/LayoutPreference/res/drawable/ic_swap_horiz_grey.xml b/packages/SettingsLib/LayoutPreference/res/drawable/ic_swap_horiz_grey.xml
new file mode 100644
index 0000000..b4145f2
--- /dev/null
+++ b/packages/SettingsLib/LayoutPreference/res/drawable/ic_swap_horiz_grey.xml
@@ -0,0 +1,25 @@
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M6.99,11L3,15l3.99,4v-3H14v-2H6.99v-3zM21,9l-3.99,-4v3H10v2h7.01v3L21,9z"
+ android:fillColor="#757575"/>
+</vector>
diff --git a/packages/SettingsLib/LayoutPreference/res/layout/cross_profiles_settings_entity_header.xml b/packages/SettingsLib/LayoutPreference/res/layout/cross_profiles_settings_entity_header.xml
new file mode 100644
index 0000000..e6f8c01
--- /dev/null
+++ b/packages/SettingsLib/LayoutPreference/res/layout/cross_profiles_settings_entity_header.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 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.
+ -->
+
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/entity_header"
+ style="@style/EntityHeader"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:orientation="horizontal">
+
+ <LinearLayout
+ android:id="@+id/entity_header_content"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:gravity="center_horizontal"
+ android:orientation="horizontal">
+
+ <LinearLayout
+ android:id="@+id/entity_header_content"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:gravity="center_horizontal"
+ android:orientation="vertical">
+
+ <ImageView
+ android:id="@+id/entity_header_icon_personal"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:scaleType="fitCenter"
+ android:antialias="true"/>
+
+ <TextView
+ android:id="@+id/install_type"
+ style="@style/TextAppearance.EntityHeaderSummary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="2dp"
+ android:text="Personal"/>
+ </LinearLayout>
+
+ <ImageView
+ android:id="@+id/entity_header_swap_horiz"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:scaleType="fitCenter"
+ android:antialias="true"
+ android:src="@drawable/ic_swap_horiz_grey"/>
+
+ <LinearLayout
+ android:id="@+id/entity_header_content"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:gravity="center_horizontal"
+ android:orientation="vertical">
+
+ <ImageView
+ android:id="@+id/entity_header_icon_work"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:scaleType="fitCenter"
+ android:antialias="true"/>
+ <TextView
+ android:id="@+id/install_type"
+ style="@style/TextAppearance.EntityHeaderSummary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="2dp"
+ android:text="Work"/>
+ </LinearLayout>
+ </LinearLayout>
+
+</RelativeLayout>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 2e7a3c0..4d96251 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -970,7 +970,7 @@
<!-- Title for the accessibility preference to configure display color space correction. [CHAR LIMIT=NONE] -->
<string name="accessibility_display_daltonizer_preference_title">Color correction</string>
<!-- Subtitle for the accessibility preference to configure display color space correction. [CHAR LIMIT=NONE] -->
- <string name="accessibility_display_daltonizer_preference_subtitle">Color correction helps people with color blindness to see more accurate colors</string>
+ <string name="accessibility_display_daltonizer_preference_subtitle">Color correction helps people with colorblindness see more accurate colors</string>
<!-- Summary shown for color space correction preference when its value is overridden by another preference [CHAR LIMIT=35] -->
<string name="daltonizer_type_overridden">Overridden by <xliff:g id="title" example="Simulate color space">%1$s</xliff:g></string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
index 3a53d29..e551b69 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
@@ -19,7 +19,8 @@
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.graphics.drawable.Drawable;
-import android.util.Log;
+import android.media.MediaRoute2Info;
+import android.media.MediaRouter2Manager;
import android.util.Pair;
import com.android.settingslib.R;
@@ -35,8 +36,9 @@
private CachedBluetoothDevice mCachedDevice;
- BluetoothMediaDevice(Context context, CachedBluetoothDevice device) {
- super(context, MediaDeviceType.TYPE_BLUETOOTH_DEVICE);
+ BluetoothMediaDevice(Context context, CachedBluetoothDevice device,
+ MediaRouter2Manager routerManager, MediaRoute2Info info, String packageName) {
+ super(context, MediaDeviceType.TYPE_BLUETOOTH_DEVICE, routerManager, info, packageName);
mCachedDevice = device;
initDeviceRecord();
}
@@ -65,20 +67,6 @@
return MediaDeviceUtils.getId(mCachedDevice);
}
- @Override
- public boolean connect() {
- //TODO(b/117129183): add callback to notify LocalMediaManager connection state.
- final boolean isConnected = mCachedDevice.setActive();
- setConnectedRecord();
- Log.d(TAG, "connect() device : " + getName() + ", is selected : " + isConnected);
- return isConnected;
- }
-
- @Override
- public void disconnect() {
- //TODO(b/117129183): disconnected last select device
- }
-
/**
* Get current CachedBluetoothDevice
*/
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaManager.java
index 3a807c9..d84788b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaManager.java
@@ -157,7 +157,7 @@
private void addMediaDevice(CachedBluetoothDevice cachedDevice) {
MediaDevice mediaDevice = findMediaDevice(MediaDeviceUtils.getId(cachedDevice));
if (mediaDevice == null) {
- mediaDevice = new BluetoothMediaDevice(mContext, cachedDevice);
+ mediaDevice = new BluetoothMediaDevice(mContext, cachedDevice, null, null, null);
cachedDevice.registerCallback(mDeviceAttributeChangeCallback);
mLastAddedDevice = mediaDevice;
mMediaDevices.add(mediaDevice);
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
index ba74139..b9081f2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
@@ -33,16 +33,9 @@
private static final String TAG = "InfoMediaDevice";
- private final MediaRoute2Info mRouteInfo;
- private final MediaRouter2Manager mRouterManager;
- private final String mPackageName;
-
InfoMediaDevice(Context context, MediaRouter2Manager routerManager, MediaRoute2Info info,
String packageName) {
- super(context, MediaDeviceType.TYPE_CAST_DEVICE);
- mRouterManager = routerManager;
- mRouteInfo = info;
- mPackageName = packageName;
+ super(context, MediaDeviceType.TYPE_CAST_DEVICE, routerManager, info, packageName);
initDeviceRecord();
}
@@ -70,13 +63,6 @@
}
@Override
- public boolean connect() {
- setConnectedRecord();
- mRouterManager.selectRoute(mPackageName, mRouteInfo);
- return true;
- }
-
- @Override
public void requestSetVolume(int volume) {
mRouterManager.requestSetVolume(mRouteInfo, volume);
}
@@ -116,12 +102,6 @@
return mContext.getResources().getString(R.string.unknown);
}
- @Override
- public void disconnect() {
- //TODO(b/144535188): disconnected last select device
- }
-
- @Override
public boolean isConnected() {
return true;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index a4be46c..e910967 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -15,13 +15,23 @@
*/
package com.android.settingslib.media;
+import static android.media.MediaRoute2Info.DEVICE_TYPE_BLUETOOTH;
+import static android.media.MediaRoute2Info.DEVICE_TYPE_REMOTE_TV;
+import static android.media.MediaRoute2Info.DEVICE_TYPE_UNKNOWN;
+
import android.app.Notification;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.media.MediaRoute2Info;
import android.media.MediaRouter2Manager;
+import android.media.RoutingSessionInfo;
import android.text.TextUtils;
+import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
import java.util.List;
import java.util.concurrent.Executor;
@@ -44,11 +54,14 @@
String mPackageName;
private MediaDevice mCurrentConnectedDevice;
+ private LocalBluetoothManager mBluetoothManager;
- public InfoMediaManager(Context context, String packageName, Notification notification) {
+ public InfoMediaManager(Context context, String packageName, Notification notification,
+ LocalBluetoothManager localBluetoothManager) {
super(context, notification);
mRouterManager = MediaRouter2Manager.getInstance(context);
+ mBluetoothManager = localBluetoothManager;
if (!TextUtils.isEmpty(packageName)) {
mPackageName = packageName;
}
@@ -94,23 +107,73 @@
private void buildAllRoutes() {
for (MediaRoute2Info route : mRouterManager.getAllRoutes()) {
- final MediaDevice device = new InfoMediaDevice(mContext, mRouterManager, route,
- mPackageName);
- mMediaDevices.add(device);
+ addMediaDevice(route);
}
}
private void buildAvailableRoutes() {
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);
+ addMediaDevice(route);
}
}
+ private void addMediaDevice(MediaRoute2Info route) {
+ final int deviceType = route.getDeviceType();
+ MediaDevice mediaDevice = null;
+ switch (deviceType) {
+ case DEVICE_TYPE_UNKNOWN:
+ //TODO(b/148765806): use correct device type once api is ready.
+ final String defaultRoute = "DEFAULT_ROUTE";
+ if (TextUtils.equals(defaultRoute, route.getOriginalId())) {
+ mediaDevice =
+ new PhoneMediaDevice(mContext, mRouterManager, route, mPackageName);
+ } else {
+ mediaDevice = new InfoMediaDevice(mContext, mRouterManager, route,
+ mPackageName);
+ if (!TextUtils.isEmpty(mPackageName)
+ && TextUtils.equals(route.getClientPackageName(), mPackageName)) {
+ mCurrentConnectedDevice = mediaDevice;
+ }
+ }
+ break;
+ case DEVICE_TYPE_REMOTE_TV:
+ break;
+ case DEVICE_TYPE_BLUETOOTH:
+ final BluetoothDevice device =
+ BluetoothAdapter.getDefaultAdapter().getRemoteDevice(route.getOriginalId());
+ final CachedBluetoothDevice cachedDevice =
+ mBluetoothManager.getCachedDeviceManager().findDevice(device);
+ mediaDevice = new BluetoothMediaDevice(mContext, cachedDevice, mRouterManager,
+ route, mPackageName);
+ break;
+ default:
+ Log.w(TAG, "addMediaDevice() unknown device type : " + deviceType);
+ break;
+
+ }
+
+ if (mediaDevice != null) {
+ mMediaDevices.add(mediaDevice);
+ }
+ }
+
+ /**
+ * Transfer MediaDevice for media without package name.
+ */
+ public boolean connectDeviceWithoutPackageName(MediaDevice device) {
+ boolean isConnected = false;
+ final List<RoutingSessionInfo> infos = mRouterManager.getActiveSessions();
+ if (infos.size() > 0) {
+ final RoutingSessionInfo info = infos.get(0);
+ final MediaRouter2Manager.RoutingController controller =
+ mRouterManager.getControllerForSession(info);
+
+ controller.transferToRoute(device.mRouteInfo);
+ isConnected = true;
+ }
+ return isConnected;
+ }
+
class RouterManagerCallback extends MediaRouter2Manager.Callback {
@Override
@@ -124,5 +187,10 @@
refreshDevices();
}
}
+
+ @Override
+ public void onRoutesChanged(List<MediaRoute2Info> routes) {
+ refreshDevices();
+ }
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index 162e9cf..a1342ec 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;
@@ -57,7 +58,6 @@
final MediaDeviceCallback mMediaDeviceCallback = new MediaDeviceCallback();
private Context mContext;
- private BluetoothMediaManager mBluetoothMediaManager;
private LocalBluetoothManager mLocalBluetoothManager;
private InfoMediaManager mInfoMediaManager;
private String mPackageName;
@@ -97,18 +97,18 @@
return;
}
- mBluetoothMediaManager =
- new BluetoothMediaManager(context, mLocalBluetoothManager, notification);
- mInfoMediaManager = new InfoMediaManager(context, packageName, notification);
+ mInfoMediaManager =
+ new InfoMediaManager(context, packageName, notification, mLocalBluetoothManager);
}
@VisibleForTesting
LocalMediaManager(Context context, LocalBluetoothManager localBluetoothManager,
- BluetoothMediaManager bluetoothMediaManager, InfoMediaManager infoMediaManager) {
+ InfoMediaManager infoMediaManager, String packageName) {
mContext = context;
mLocalBluetoothManager = localBluetoothManager;
- mBluetoothMediaManager = bluetoothMediaManager;
mInfoMediaManager = infoMediaManager;
+ mPackageName = packageName;
+
}
/**
@@ -135,7 +135,12 @@
mCurrentConnectedDevice.disconnect();
}
- final boolean isConnected = device.connect();
+ boolean isConnected = false;
+ if (TextUtils.isEmpty(mPackageName)) {
+ isConnected = mInfoMediaManager.connectDeviceWithoutPackageName(device);
+ } else {
+ isConnected = device.connect();
+ }
if (isConnected) {
mCurrentConnectedDevice = device;
}
@@ -159,29 +164,10 @@
*/
public void startScan() {
mMediaDevices.clear();
- mBluetoothMediaManager.registerCallback(mMediaDeviceCallback);
- mBluetoothMediaManager.startScan();
mInfoMediaManager.registerCallback(mMediaDeviceCallback);
mInfoMediaManager.startScan();
}
- private void addPhoneDeviceIfNecessary() {
- // add phone device to list if there have any Bluetooth device and cast device.
- if (mMediaDevices.size() > 0 && !mMediaDevices.contains(mPhoneDevice)) {
- if (mPhoneDevice == null) {
- mPhoneDevice = new PhoneMediaDevice(mContext, mLocalBluetoothManager);
- }
- mMediaDevices.add(mPhoneDevice);
- }
- }
-
- private void removePhoneMediaDeviceIfNecessary() {
- // if PhoneMediaDevice is the last item in the list, remove it.
- if (mMediaDevices.size() == 1 && mMediaDevices.contains(mPhoneDevice)) {
- mMediaDevices.clear();
- }
- }
-
void dispatchDeviceListUpdate() {
synchronized (mCallbacks) {
Collections.sort(mMediaDevices, COMPARATOR);
@@ -203,8 +189,6 @@
* Stop scan MediaDevice
*/
public void stopScan() {
- mBluetoothMediaManager.unregisterCallback(mMediaDeviceCallback);
- mBluetoothMediaManager.stopScan();
mInfoMediaManager.unregisterCallback(mMediaDeviceCallback);
mInfoMediaManager.stopScan();
}
@@ -268,14 +252,17 @@
}
private MediaDevice updateCurrentConnectedDevice() {
+ MediaDevice phoneMediaDevice = null;
for (MediaDevice device : mMediaDevices) {
if (device instanceof BluetoothMediaDevice) {
if (isConnected(((BluetoothMediaDevice) device).getCachedDevice())) {
return device;
}
+ } else if (device instanceof PhoneMediaDevice) {
+ phoneMediaDevice = device;
}
}
- return mMediaDevices.contains(mPhoneDevice) ? mPhoneDevice : null;
+ return mMediaDevices.contains(phoneMediaDevice) ? phoneMediaDevice : null;
}
private boolean isConnected(CachedBluetoothDevice device) {
@@ -288,38 +275,24 @@
public void onDeviceAdded(MediaDevice device) {
if (!mMediaDevices.contains(device)) {
mMediaDevices.add(device);
- addPhoneDeviceIfNecessary();
dispatchDeviceListUpdate();
}
}
@Override
public void onDeviceListAdded(List<MediaDevice> devices) {
- for (MediaDevice device : devices) {
- if (getMediaDeviceById(mMediaDevices, device.getId()) == null) {
- mMediaDevices.add(device);
- }
- }
- addPhoneDeviceIfNecessary();
+ mMediaDevices.clear();
+ mMediaDevices.addAll(devices);
final MediaDevice infoMediaDevice = mInfoMediaManager.getCurrentConnectedDevice();
mCurrentConnectedDevice = infoMediaDevice != null
? infoMediaDevice : updateCurrentConnectedDevice();
- updatePhoneMediaDeviceSummary();
dispatchDeviceListUpdate();
}
- private void updatePhoneMediaDeviceSummary() {
- if (mPhoneDevice != null) {
- ((PhoneMediaDevice) mPhoneDevice)
- .updateSummary(mCurrentConnectedDevice == mPhoneDevice);
- }
- }
-
@Override
public void onDeviceRemoved(MediaDevice device) {
if (mMediaDevices.contains(device)) {
mMediaDevices.remove(device);
- removePhoneMediaDeviceIfNecessary();
dispatchDeviceListUpdate();
}
}
@@ -327,7 +300,6 @@
@Override
public void onDeviceListRemoved(List<MediaDevice> devices) {
mMediaDevices.removeAll(devices);
- removePhoneMediaDeviceIfNecessary();
dispatchDeviceListUpdate();
}
@@ -340,7 +312,6 @@
return;
}
mCurrentConnectedDevice = connectDevice;
- updatePhoneMediaDeviceSummary();
dispatchDeviceAttributesChanged();
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
index 839d528..580e086 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
@@ -17,9 +17,12 @@
import android.content.Context;
import android.graphics.drawable.Drawable;
+import android.media.MediaRoute2Info;
+import android.media.MediaRouter2Manager;
import android.text.TextUtils;
import androidx.annotation.IntDef;
+import androidx.annotation.VisibleForTesting;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -40,14 +43,23 @@
int TYPE_BLUETOOTH_DEVICE = 3;
}
+ @VisibleForTesting
+ int mType;
+
private int mConnectedRecord;
- protected Context mContext;
- protected int mType;
+ protected final Context mContext;
+ protected final MediaRoute2Info mRouteInfo;
+ protected final MediaRouter2Manager mRouterManager;
+ protected final String mPackageName;
- MediaDevice(Context context, @MediaDeviceType int type) {
+ MediaDevice(Context context, @MediaDeviceType int type, MediaRouter2Manager routerManager,
+ MediaRoute2Info info, String packageName) {
mType = type;
mContext = context;
+ mRouteInfo = info;
+ mRouterManager = routerManager;
+ mPackageName = packageName;
}
void initDeviceRecord() {
@@ -83,13 +95,6 @@
*/
public abstract String getId();
- /**
- * Transfer MediaDevice for media
- *
- * @return result of transfer media
- */
- public abstract boolean connect();
-
void setConnectedRecord() {
mConnectedRecord++;
ConnectionRecordManager.getInstance().setConnectionRecord(mContext, getId(),
@@ -97,11 +102,6 @@
}
/**
- * Stop transfer MediaDevice
- */
- public abstract void disconnect();
-
- /**
* According the MediaDevice type to check whether we are connected to this MediaDevice.
*
* @return Whether it is connected.
@@ -162,6 +162,23 @@
}
/**
+ * Transfer MediaDevice for media
+ *
+ * @return result of transfer media
+ */
+ public boolean connect() {
+ setConnectedRecord();
+ mRouterManager.selectRoute(mPackageName, mRouteInfo);
+ return true;
+ }
+
+ /**
+ * Stop transfer MediaDevice
+ */
+ public void disconnect() {
+ }
+
+ /**
* Rules:
* 1. If there is one of the connected devices identified as a carkit, this carkit will
* be always on the top of the device list. Rule 2 and Rule 3 can’t overrule this rule.
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
index af91c34..166fbaa 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
@@ -17,14 +17,11 @@
import android.content.Context;
import android.graphics.drawable.Drawable;
-import android.util.Log;
+import android.media.MediaRoute2Info;
+import android.media.MediaRouter2Manager;
import com.android.settingslib.R;
-import com.android.settingslib.bluetooth.A2dpProfile;
import com.android.settingslib.bluetooth.BluetoothUtils;
-import com.android.settingslib.bluetooth.HearingAidProfile;
-import com.android.settingslib.bluetooth.LocalBluetoothManager;
-import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
/**
* PhoneMediaDevice extends MediaDevice to represents Phone device.
@@ -35,15 +32,12 @@
public static final String ID = "phone_media_device_id_1";
- private LocalBluetoothProfileManager mProfileManager;
- private LocalBluetoothManager mLocalBluetoothManager;
private String mSummary = "";
- PhoneMediaDevice(Context context, LocalBluetoothManager localBluetoothManager) {
- super(context, MediaDeviceType.TYPE_PHONE_DEVICE);
+ PhoneMediaDevice(Context context, MediaRouter2Manager routerManager, MediaRoute2Info info,
+ String packageName) {
+ super(context, MediaDeviceType.TYPE_PHONE_DEVICE, routerManager, info, packageName);
- mLocalBluetoothManager = localBluetoothManager;
- mProfileManager = mLocalBluetoothManager.getProfileManager();
initDeviceRecord();
}
@@ -69,32 +63,6 @@
}
@Override
- public boolean connect() {
- final HearingAidProfile hapProfile = mProfileManager.getHearingAidProfile();
- final A2dpProfile a2dpProfile = mProfileManager.getA2dpProfile();
-
- // Some device may not have HearingAidProfile, consider all situation to set active device.
- boolean isConnected = false;
- if (hapProfile != null && a2dpProfile != null) {
- isConnected = hapProfile.setActiveDevice(null) && a2dpProfile.setActiveDevice(null);
- } else if (a2dpProfile != null) {
- isConnected = a2dpProfile.setActiveDevice(null);
- } else if (hapProfile != null) {
- isConnected = hapProfile.setActiveDevice(null);
- }
- updateSummary(isConnected);
- setConnectedRecord();
-
- Log.d(TAG, "connect() device : " + getName() + ", is selected : " + isConnected);
- return isConnected;
- }
-
- @Override
- public void disconnect() {
- updateSummary(false);
- }
-
- @Override
public boolean isConnected() {
return true;
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/BluetoothMediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/BluetoothMediaDeviceTest.java
index e0e2fd6..a39bcb7 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/BluetoothMediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/BluetoothMediaDeviceTest.java
@@ -51,21 +51,7 @@
when(mDevice.isActiveDevice(BluetoothProfile.A2DP)).thenReturn(true);
when(mDevice.isActiveDevice(BluetoothProfile.HEARING_AID)).thenReturn(true);
- mBluetoothMediaDevice = new BluetoothMediaDevice(mContext, mDevice);
- }
-
- @Test
- public void connect_setActiveSuccess_isConnectedReturnTrue() {
- when(mDevice.setActive()).thenReturn(true);
-
- assertThat(mBluetoothMediaDevice.connect()).isTrue();
- }
-
- @Test
- public void connect_setActiveFail_isConnectedReturnFalse() {
- when(mDevice.setActive()).thenReturn(false);
-
- assertThat(mBluetoothMediaDevice.connect()).isFalse();
+ mBluetoothMediaDevice = new BluetoothMediaDevice(mContext, mDevice, null, null, null);
}
@Test
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
index 2e304dc..04ceb21 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java
@@ -18,7 +18,6 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
@@ -109,13 +108,6 @@
}
@Test
- public void connect_shouldSelectRoute() {
- mInfoMediaDevice.connect();
-
- verify(mRouterManager).selectRoute(TEST_PACKAGE_NAME, mRouteInfo);
- }
-
- @Test
public void getClientPackageName_returnPackageName() {
when(mRouteInfo.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
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 3726fb2..05f5fa0 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
@@ -25,6 +25,9 @@
import android.content.Context;
import android.media.MediaRoute2Info;
import android.media.MediaRouter2Manager;
+import android.media.RoutingSessionInfo;
+
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
import org.junit.Before;
import org.junit.Test;
@@ -45,6 +48,8 @@
@Mock
private MediaRouter2Manager mRouterManager;
+ @Mock
+ private LocalBluetoothManager mLocalBluetoothManager;
private InfoMediaManager mInfoMediaManager;
private Context mContext;
@@ -54,7 +59,8 @@
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
- mInfoMediaManager = new InfoMediaManager(mContext, TEST_PACKAGE_NAME, null);
+ mInfoMediaManager =
+ new InfoMediaManager(mContext, TEST_PACKAGE_NAME, null, mLocalBluetoothManager);
mInfoMediaManager.mRouterManager = mRouterManager;
}
@@ -142,4 +148,59 @@
assertThat(mInfoMediaManager.mMediaDevices).hasSize(0);
}
+
+ @Test
+ public void onRoutesChanged_getAvailableRoutes_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.onRoutesChanged(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 onRoutesChanged_buildAllRoutes_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.getAllRoutes()).thenReturn(routes);
+
+ final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID);
+ assertThat(mediaDevice).isNull();
+
+ mInfoMediaManager.mPackageName = "";
+ mInfoMediaManager.mMediaRouterCallback.onRoutesChanged(routes);
+
+ final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0);
+ assertThat(infoDevice.getId()).isEqualTo(TEST_ID);
+ assertThat(mInfoMediaManager.mMediaDevices).hasSize(routes.size());
+ }
+
+ @Test
+ public void connectDeviceWithoutPackageName_noSession_returnFalse() {
+ final MediaRoute2Info info = mock(MediaRoute2Info.class);
+ final MediaDevice device = new InfoMediaDevice(mContext, mRouterManager, info,
+ TEST_PACKAGE_NAME);
+
+ final List<RoutingSessionInfo> infos = new ArrayList<>();
+
+ when(mRouterManager.getActiveSessions()).thenReturn(infos);
+
+ assertThat(mInfoMediaManager.connectDeviceWithoutPackageName(device)).isFalse();
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
index 15aaa82..3d67ba0 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
@@ -80,7 +80,7 @@
when(mLocalProfileManager.getHearingAidProfile()).thenReturn(mHapProfile);
mLocalMediaManager = new LocalMediaManager(mContext, mLocalBluetoothManager,
- mBluetoothMediaManager, mInfoMediaManager);
+ mInfoMediaManager, "com.test.packagename");
}
@Test
@@ -163,14 +163,14 @@
}
@Test
- public void onDeviceAdded_mediaDeviceAndPhoneDeviceNotExistInList_addBothDevice() {
+ public void onDeviceAdded_addDevice() {
final MediaDevice device = mock(MediaDevice.class);
assertThat(mLocalMediaManager.mMediaDevices).isEmpty();
mLocalMediaManager.registerCallback(mCallback);
mLocalMediaManager.mMediaDeviceCallback.onDeviceAdded(device);
- assertThat(mLocalMediaManager.mMediaDevices).hasSize(2);
+ assertThat(mLocalMediaManager.mMediaDevices).hasSize(1);
verify(mCallback).onDeviceListUpdate(any());
}
@@ -206,7 +206,7 @@
}
@Test
- public void onDeviceListAdded_phoneDeviceNotExistInList_addPhoneDeviceAndDevicesList() {
+ public void onDeviceListAdded_addDevicesList() {
final List<MediaDevice> devices = new ArrayList<>();
final MediaDevice device1 = mock(MediaDevice.class);
final MediaDevice device2 = mock(MediaDevice.class);
@@ -220,12 +220,12 @@
mLocalMediaManager.registerCallback(mCallback);
mLocalMediaManager.mMediaDeviceCallback.onDeviceListAdded(devices);
- assertThat(mLocalMediaManager.mMediaDevices).hasSize(3);
+ assertThat(mLocalMediaManager.mMediaDevices).hasSize(2);
verify(mCallback).onDeviceListUpdate(any());
}
@Test
- public void onDeviceListAdded_phoneDeviceExistInList_addDeviceList() {
+ public void onDeviceListAdded_addDeviceList() {
final List<MediaDevice> devices = new ArrayList<>();
final MediaDevice device1 = mock(MediaDevice.class);
final MediaDevice device2 = mock(MediaDevice.class);
@@ -245,12 +245,12 @@
mLocalMediaManager.registerCallback(mCallback);
mLocalMediaManager.mMediaDeviceCallback.onDeviceListAdded(devices);
- assertThat(mLocalMediaManager.mMediaDevices).hasSize(4);
+ assertThat(mLocalMediaManager.mMediaDevices).hasSize(2);
verify(mCallback).onDeviceListUpdate(any());
}
@Test
- public void onDeviceRemoved_phoneDeviceIsLastDeviceAfterRemoveMediaDevice_removeBothDevice() {
+ public void onDeviceRemoved_removeDevice() {
final MediaDevice device1 = mock(MediaDevice.class);
mLocalMediaManager.mPhoneDevice = mock(PhoneMediaDevice.class);
mLocalMediaManager.mMediaDevices.add(device1);
@@ -260,7 +260,7 @@
mLocalMediaManager.registerCallback(mCallback);
mLocalMediaManager.mMediaDeviceCallback.onDeviceRemoved(device1);
- assertThat(mLocalMediaManager.mMediaDevices).isEmpty();
+ assertThat(mLocalMediaManager.mMediaDevices).hasSize(1);
verify(mCallback).onDeviceListUpdate(any());
}
@@ -298,7 +298,7 @@
}
@Test
- public void onDeviceListRemoved_phoneDeviceIsLastDeviceAfterRemoveDeviceList_removeAll() {
+ public void onDeviceListRemoved_removeAll() {
final List<MediaDevice> devices = new ArrayList<>();
final MediaDevice device1 = mock(MediaDevice.class);
final MediaDevice device2 = mock(MediaDevice.class);
@@ -311,7 +311,8 @@
assertThat(mLocalMediaManager.mMediaDevices).hasSize(3);
mLocalMediaManager.registerCallback(mCallback);
- mLocalMediaManager.mMediaDeviceCallback.onDeviceListRemoved(devices);
+ mLocalMediaManager.mMediaDeviceCallback
+ .onDeviceListRemoved(mLocalMediaManager.mMediaDevices);
assertThat(mLocalMediaManager.mMediaDevices).isEmpty();
verify(mCallback).onDeviceListUpdate(any());
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 02cb83e..fb8b78b 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
@@ -17,6 +17,7 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothClass;
@@ -83,6 +84,14 @@
@Mock
private MediaRoute2Info mRouteInfo3;
@Mock
+ private MediaRoute2Info mBluetoothRouteInfo1;
+ @Mock
+ private MediaRoute2Info mBluetoothRouteInfo2;
+ @Mock
+ private MediaRoute2Info mBluetoothRouteInfo3;
+ @Mock
+ private MediaRoute2Info mPhoneRouteInfo;
+ @Mock
private LocalBluetoothProfileManager mProfileManager;
@Mock
private HearingAidProfile mHapProfile;
@@ -90,6 +99,8 @@
private A2dpProfile mA2dpProfile;
@Mock
private BluetoothDevice mDevice;
+ @Mock
+ private MediaRouter2Manager mMediaRouter2Manager;
private BluetoothMediaDevice mBluetoothMediaDevice1;
private BluetoothMediaDevice mBluetoothMediaDevice2;
@@ -100,7 +111,6 @@
private InfoMediaDevice mInfoMediaDevice3;
private List<MediaDevice> mMediaDevices = new ArrayList<>();
private PhoneMediaDevice mPhoneMediaDevice;
- private MediaRouter2Manager mMediaRouter2Manager;
@Before
public void setUp() {
@@ -133,17 +143,24 @@
when(mProfileManager.getHearingAidProfile()).thenReturn(mHapProfile);
when(mA2dpProfile.getActiveDevice()).thenReturn(mDevice);
- mBluetoothMediaDevice1 = new BluetoothMediaDevice(mContext, mCachedDevice1);
- mBluetoothMediaDevice2 = new BluetoothMediaDevice(mContext, mCachedDevice2);
- mBluetoothMediaDevice3 = new BluetoothMediaDevice(mContext, mCachedDevice3);
- mMediaRouter2Manager = MediaRouter2Manager.getInstance(mContext);
+ mBluetoothMediaDevice1 =
+ new BluetoothMediaDevice(mContext, mCachedDevice1, mMediaRouter2Manager,
+ mBluetoothRouteInfo1, TEST_PACKAGE_NAME);
+ mBluetoothMediaDevice2 =
+ new BluetoothMediaDevice(mContext, mCachedDevice2, mMediaRouter2Manager,
+ mBluetoothRouteInfo2, TEST_PACKAGE_NAME);
+ mBluetoothMediaDevice3 =
+ new BluetoothMediaDevice(mContext, mCachedDevice3, mMediaRouter2Manager,
+ mBluetoothRouteInfo3, TEST_PACKAGE_NAME);
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);
+ mPhoneMediaDevice =
+ new PhoneMediaDevice(mContext, mMediaRouter2Manager, mPhoneRouteInfo,
+ TEST_PACKAGE_NAME);
}
@Test
@@ -370,4 +387,11 @@
assertThat(mMediaDevices.get(5)).isEqualTo(mBluetoothMediaDevice1);
assertThat(mMediaDevices.get(6)).isEqualTo(mBluetoothMediaDevice2);
}
+
+ @Test
+ public void connect_shouldSelectRoute() {
+ mInfoMediaDevice1.connect();
+
+ verify(mMediaRouter2Manager).selectRoute(TEST_PACKAGE_NAME, mRouteInfo1);
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/PhoneMediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/PhoneMediaDeviceTest.java
index 0752dc0..db984fb 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/PhoneMediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/PhoneMediaDeviceTest.java
@@ -18,21 +18,13 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.when;
-
-import android.bluetooth.BluetoothDevice;
import android.content.Context;
import com.android.settingslib.R;
-import com.android.settingslib.bluetooth.A2dpProfile;
-import com.android.settingslib.bluetooth.HearingAidProfile;
-import com.android.settingslib.bluetooth.LocalBluetoothManager;
-import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
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;
@@ -40,17 +32,6 @@
@RunWith(RobolectricTestRunner.class)
public class PhoneMediaDeviceTest {
- @Mock
- private LocalBluetoothProfileManager mLocalProfileManager;
- @Mock
- private LocalBluetoothManager mLocalBluetoothManager;
- @Mock
- private HearingAidProfile mHapProfile;
- @Mock
- private A2dpProfile mA2dpProfile;
- @Mock
- private BluetoothDevice mDevice;
-
private Context mContext;
private PhoneMediaDevice mPhoneMediaDevice;
@@ -59,68 +40,8 @@
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
- when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalProfileManager);
- when(mLocalProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile);
- when(mLocalProfileManager.getHearingAidProfile()).thenReturn(mHapProfile);
- when(mA2dpProfile.getActiveDevice()).thenReturn(mDevice);
-
- mPhoneMediaDevice = new PhoneMediaDevice(mContext, mLocalBluetoothManager);
- }
-
- @Test
- public void connect_phoneDeviceSetActiveSuccess_isConnectedReturnTrue() {
- when(mA2dpProfile.setActiveDevice(null)).thenReturn(true);
- when(mHapProfile.setActiveDevice(null)).thenReturn(true);
-
- assertThat(mPhoneMediaDevice.connect()).isTrue();
- }
-
- @Test
- public void connect_a2dpProfileSetActiveFail_isConnectedReturnFalse() {
- when(mA2dpProfile.setActiveDevice(null)).thenReturn(false);
- when(mHapProfile.setActiveDevice(null)).thenReturn(true);
-
- assertThat(mPhoneMediaDevice.connect()).isFalse();
- }
-
- @Test
- public void connect_hearingAidProfileSetActiveFail_isConnectedReturnFalse() {
- when(mA2dpProfile.setActiveDevice(null)).thenReturn(true);
- when(mHapProfile.setActiveDevice(null)).thenReturn(false);
-
- assertThat(mPhoneMediaDevice.connect()).isFalse();
- }
-
- @Test
- public void connect_hearingAidAndA2dpProfileSetActiveFail_isConnectedReturnFalse() {
- when(mA2dpProfile.setActiveDevice(null)).thenReturn(false);
- when(mHapProfile.setActiveDevice(null)).thenReturn(false);
-
- assertThat(mPhoneMediaDevice.connect()).isFalse();
- }
-
- @Test
- public void connect_hearingAidProfileIsNullAndA2dpProfileNotNull_isConnectedReturnTrue() {
- when(mLocalProfileManager.getHearingAidProfile()).thenReturn(null);
-
- when(mA2dpProfile.setActiveDevice(null)).thenReturn(true);
- assertThat(mPhoneMediaDevice.connect()).isTrue();
- }
-
- @Test
- public void connect_hearingAidProfileNotNullAndA2dpProfileIsNull_isConnectedReturnTrue() {
- when(mLocalProfileManager.getA2dpProfile()).thenReturn(null);
-
- when(mHapProfile.setActiveDevice(null)).thenReturn(true);
- assertThat(mPhoneMediaDevice.connect()).isTrue();
- }
-
- @Test
- public void connect_hearingAidProfileAndA2dpProfileIsNull_isConnectedReturnFalse() {
- when(mLocalProfileManager.getA2dpProfile()).thenReturn(null);
- when(mLocalProfileManager.getHearingAidProfile()).thenReturn(null);
-
- assertThat(mPhoneMediaDevice.connect()).isFalse();
+ mPhoneMediaDevice =
+ new PhoneMediaDevice(mContext, null, null, null);
}
@Test
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 1d679c7..5946f21 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -308,6 +308,22 @@
android:excludeFromRecents="true"
android:exported="false" />
+ <!--
+ The following is used as a no-op/null home activity when
+ no other MAIN/HOME activity is present (e.g., in CSI).
+ -->
+ <activity android:name=".NullHome"
+ android:excludeFromRecents="true"
+ android:label=""
+ android:screenOrientation="nosensor">
+ <!-- The priority here is set to be lower than that for Settings -->
+ <intent-filter android:priority="-1100">
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.HOME" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
<receiver
android:name=".BugreportRequestedReceiver"
android:permission="android.permission.TRIGGER_SHELL_BUGREPORT">
diff --git a/packages/Shell/res/layout/null_home_finishing_boot.xml b/packages/Shell/res/layout/null_home_finishing_boot.xml
new file mode 100644
index 0000000..5f9563a
--- /dev/null
+++ b/packages/Shell/res/layout/null_home_finishing_boot.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 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
+ -->
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="#80000000"
+ android:forceHasOverlappingRendering="false">
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_gravity="center"
+ android:layout_marginStart="16dp"
+ android:layout_marginEnd="16dp">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="40sp"
+ android:textColor="?android:attr/textColorPrimary"
+ android:text="@*android:string/android_start_title"/>
+ <ProgressBar
+ style="@android:style/Widget.Material.ProgressBar.Horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="12.75dp"
+ android:colorControlActivated="?android:attr/textColorPrimary"
+ android:indeterminate="true"/>
+ </LinearLayout>
+</FrameLayout>
diff --git a/packages/Shell/src/com/android/shell/NullHome.java b/packages/Shell/src/com/android/shell/NullHome.java
new file mode 100644
index 0000000..bd97561
--- /dev/null
+++ b/packages/Shell/src/com/android/shell/NullHome.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2020 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.shell;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+
+/**
+ * This covers the fallback case where no launcher is available.
+ * Usually Settings.apk has one fallback home activity.
+ * Settings.apk, however, is not part of CSI, which needs to be
+ * standalone (bootable and testable).
+ */
+public class NullHome extends Activity {
+ private static final String TAG = "NullHome";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Log.i(TAG, "onCreate");
+ setContentView(R.layout.null_home_finishing_boot);
+ }
+
+ protected void onDestroy() {
+ super.onDestroy();
+ Log.i(TAG, "onDestroy");
+ }
+}
diff --git a/packages/Tethering/common/TetheringLib/Android.bp b/packages/Tethering/common/TetheringLib/Android.bp
index e0adb34d..8c4f733 100644
--- a/packages/Tethering/common/TetheringLib/Android.bp
+++ b/packages/Tethering/common/TetheringLib/Android.bp
@@ -59,16 +59,33 @@
],
hostdex: true, // for hiddenapi check
- visibility: [
- "//frameworks/base/packages/Tethering:__subpackages__",
- //TODO(b/147200698) remove below lines when the platform is built with stubs
- "//frameworks/base",
- "//frameworks/base/services",
- "//frameworks/base/services/core",
- ],
+ visibility: ["//frameworks/base/packages/Tethering:__subpackages__"],
apex_available: ["com.android.tethering"],
}
+droidstubs {
+ name: "framework-tethering-stubs-sources",
+ defaults: ["framework-module-stubs-defaults-module_libs_api"],
+ srcs: [
+ "src/android/net/TetheredClient.java",
+ "src/android/net/TetheringManager.java",
+ "src/android/net/TetheringConstants.java",
+ ],
+ libs: [
+ "tethering-aidl-interfaces-java",
+ "framework-all",
+ ],
+ sdk_version: "core_platform",
+}
+
+java_library {
+ name: "framework-tethering-stubs",
+ srcs: [":framework-tethering-stubs-sources"],
+ libs: ["framework-all"],
+ static_libs: ["tethering-aidl-interfaces-java"],
+ sdk_version: "core_platform",
+}
+
filegroup {
name: "framework-tethering-srcs",
srcs: [
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java
index 6514688..ca5ef09 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java
@@ -16,6 +16,8 @@
package android.net;
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -34,6 +36,7 @@
* @hide
*/
@SystemApi
+@SystemApi(client = MODULE_LIBRARIES)
@TestApi
public final class TetheredClient implements Parcelable {
@NonNull
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java
index 00cf98e..df87ac9 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java
@@ -16,6 +16,9 @@
package android.net;
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
import android.os.ResultReceiver;
/**
@@ -28,39 +31,30 @@
* symbols from framework-tethering even when they are in a non-hidden class.
* @hide
*/
+@SystemApi(client = MODULE_LIBRARIES)
public class TetheringConstants {
/**
* Extra used for communicating with the TetherService. Includes the type of tethering to
* enable if any.
- *
- * {@hide}
*/
public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType";
/**
* Extra used for communicating with the TetherService. Includes the type of tethering for
* which to cancel provisioning.
- *
- * {@hide}
*/
public static final String EXTRA_REM_TETHER_TYPE = "extraRemTetherType";
/**
* Extra used for communicating with the TetherService. True to schedule a recheck of tether
* provisioning.
- *
- * {@hide}
*/
public static final String EXTRA_SET_ALARM = "extraSetAlarm";
/**
* Tells the TetherService to run a provision check now.
- *
- * {@hide}
*/
public static final String EXTRA_RUN_PROVISION = "extraRunProvision";
/**
* Extra used for communicating with the TetherService. Contains the {@link ResultReceiver}
* which will receive provisioning results. Can be left empty.
- *
- * {@hide}
*/
public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback";
}
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
index 53a358f..6a9f010 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
@@ -15,6 +15,8 @@
*/
package android.net;
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -50,6 +52,7 @@
* @hide
*/
@SystemApi
+@SystemApi(client = MODULE_LIBRARIES)
@TestApi
public class TetheringManager {
private static final String TAG = TetheringManager.class.getSimpleName();
@@ -177,6 +180,7 @@
* service is not connected.
* {@hide}
*/
+ @SystemApi(client = MODULE_LIBRARIES)
public TetheringManager(@NonNull final Context context,
@NonNull Supplier<IBinder> connectorSupplier) {
mContext = context;
@@ -395,6 +399,7 @@
* {@hide}
*/
@Deprecated
+ @SystemApi(client = MODULE_LIBRARIES)
public int tether(@NonNull final String iface) {
final String callerPkg = mContext.getOpPackageName();
Log.i(TAG, "tether caller:" + callerPkg);
@@ -418,6 +423,7 @@
* {@hide}
*/
@Deprecated
+ @SystemApi(client = MODULE_LIBRARIES)
public int untether(@NonNull final String iface) {
final String callerPkg = mContext.getOpPackageName();
Log.i(TAG, "untether caller:" + callerPkg);
@@ -444,6 +450,7 @@
* {@hide}
*/
@Deprecated
+ @SystemApi(client = MODULE_LIBRARIES)
public int setUsbTethering(final boolean enable) {
final String callerPkg = mContext.getOpPackageName();
Log.i(TAG, "setUsbTethering caller:" + callerPkg);
@@ -702,6 +709,7 @@
* {@hide}
*/
// TODO: improve the usage of ResultReceiver, b/145096122
+ @SystemApi(client = MODULE_LIBRARIES)
public void requestLatestTetheringEntitlementResult(final int type,
@NonNull final ResultReceiver receiver, final boolean showEntitlementUi) {
final String callerPkg = mContext.getOpPackageName();
@@ -982,6 +990,7 @@
* interface
* @hide
*/
+ @SystemApi(client = MODULE_LIBRARIES)
public int getLastTetherError(@NonNull final String iface) {
mCallback.waitForStarted();
if (mTetherStatesParcel == null) return TETHER_ERROR_NO_ERROR;
@@ -1004,6 +1013,7 @@
* what interfaces are considered tetherable usb interfaces.
* @hide
*/
+ @SystemApi(client = MODULE_LIBRARIES)
public @NonNull String[] getTetherableUsbRegexs() {
mCallback.waitForStarted();
return mTetheringConfiguration.tetherableUsbRegexs;
@@ -1018,6 +1028,7 @@
* what interfaces are considered tetherable wifi interfaces.
* @hide
*/
+ @SystemApi(client = MODULE_LIBRARIES)
public @NonNull String[] getTetherableWifiRegexs() {
mCallback.waitForStarted();
return mTetheringConfiguration.tetherableWifiRegexs;
@@ -1032,6 +1043,7 @@
* what interfaces are considered tetherable bluetooth interfaces.
* @hide
*/
+ @SystemApi(client = MODULE_LIBRARIES)
public @NonNull String[] getTetherableBluetoothRegexs() {
mCallback.waitForStarted();
return mTetheringConfiguration.tetherableBluetoothRegexs;
@@ -1044,6 +1056,7 @@
* @return an array of 0 or more Strings of tetherable interface names.
* @hide
*/
+ @SystemApi(client = MODULE_LIBRARIES)
public @NonNull String[] getTetherableIfaces() {
mCallback.waitForStarted();
if (mTetherStatesParcel == null) return new String[0];
@@ -1057,6 +1070,7 @@
* @return an array of 0 or more String of currently tethered interface names.
* @hide
*/
+ @SystemApi(client = MODULE_LIBRARIES)
public @NonNull String[] getTetheredIfaces() {
mCallback.waitForStarted();
if (mTetherStatesParcel == null) return new String[0];
@@ -1076,6 +1090,7 @@
* which failed to tether.
* @hide
*/
+ @SystemApi(client = MODULE_LIBRARIES)
public @NonNull String[] getTetheringErroredIfaces() {
mCallback.waitForStarted();
if (mTetherStatesParcel == null) return new String[0];
@@ -1103,6 +1118,7 @@
* @return a boolean - {@code true} indicating Tethering is supported.
* @hide
*/
+ @SystemApi(client = MODULE_LIBRARIES)
public boolean isTetheringSupported() {
final String callerPkg = mContext.getOpPackageName();
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
index 39c402b..64c16e4 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
@@ -1944,7 +1944,8 @@
parcel.tetheringSupported = mDeps.isTetheringSupported();
parcel.upstreamNetwork = mTetherUpstream;
parcel.config = mConfig.toStableParcelable();
- parcel.states = mTetherStatesParcel;
+ parcel.states =
+ mTetherStatesParcel != null ? mTetherStatesParcel : emptyTetherStatesParcel();
try {
callback.onCallbackStarted(parcel);
} catch (RemoteException e) {
@@ -1953,6 +1954,17 @@
});
}
+ private TetherStatesParcel emptyTetherStatesParcel() {
+ final TetherStatesParcel parcel = new TetherStatesParcel();
+ parcel.availableList = new String[0];
+ parcel.tetheredList = new String[0];
+ parcel.localOnlyList = new String[0];
+ parcel.erroredIfaceList = new String[0];
+ parcel.lastErrorList = new int[0];
+
+ return parcel;
+ }
+
/** Unregister tethering event callback */
void unregisterTetheringEventCallback(ITetheringEventCallback callback) {
mHandler.post(() -> {
diff --git a/packages/Tethering/tests/unit/Android.bp b/packages/Tethering/tests/unit/Android.bp
index 13174c5..c6905ec 100644
--- a/packages/Tethering/tests/unit/Android.bp
+++ b/packages/Tethering/tests/unit/Android.bp
@@ -34,7 +34,15 @@
"TetheringApiCurrentLib",
"testables",
],
+ // TODO(b/147200698) change sdk_version to module-current and
+ // remove framework-minus-apex, ext, and framework-res
+ sdk_version: "core_platform",
libs: [
+ "framework-minus-apex",
+ "ext",
+ "framework-res",
+ "framework-wifi-stubs",
+ "framework-telephony-stubs",
"android.test.runner",
"android.test.base",
"android.test.mock",
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
index 4710287..6d49e20 100644
--- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
@@ -127,6 +127,7 @@
import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.networkstack.tethering.R;
+import com.android.testutils.MiscAssertsKt;
import org.junit.After;
import org.junit.Before;
@@ -1220,6 +1221,16 @@
}
}
+ private void assertTetherStatesNotNullButEmpty(final TetherStatesParcel parcel) {
+ assertFalse(parcel == null);
+ assertEquals(0, parcel.availableList.length);
+ assertEquals(0, parcel.tetheredList.length);
+ assertEquals(0, parcel.localOnlyList.length);
+ assertEquals(0, parcel.erroredIfaceList.length);
+ assertEquals(0, parcel.lastErrorList.length);
+ MiscAssertsKt.assertFieldCountEquals(5, TetherStatesParcel.class);
+ }
+
@Test
public void testRegisterTetheringEventCallback() throws Exception {
TestTetheringEventCallback callback = new TestTetheringEventCallback();
@@ -1232,7 +1243,7 @@
callback.expectConfigurationChanged(
mTethering.getTetheringConfiguration().toStableParcelable());
TetherStatesParcel tetherState = callback.pollTetherStatesChanged();
- assertEquals(tetherState, null);
+ assertTetherStatesNotNullButEmpty(tetherState);
// 2. Enable wifi tethering.
UpstreamNetworkState upstreamState = buildMobileDualStackUpstreamState();
when(mUpstreamNetworkMonitor.getCurrentPreferredUpstream()).thenReturn(upstreamState);
diff --git a/services/Android.bp b/services/Android.bp
index 28c8aee..32394f4 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -75,7 +75,7 @@
libs: [
"android.hidl.manager-V1.0-java",
- "framework-tethering"
+ "framework-tethering-stubs",
],
plugins: [
diff --git a/services/core/Android.bp b/services/core/Android.bp
index f33237f..6fc6084 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -98,7 +98,7 @@
"android.hardware.tv.cec-V1.0-java",
"android.hardware.vibrator-java",
"app-compat-annotations",
- "framework-tethering",
+ "framework-tethering-stubs",
],
required: [
diff --git a/services/core/java/com/android/server/display/WifiDisplayController.java b/services/core/java/com/android/server/display/WifiDisplayController.java
index 2992f1e..a7e1a28 100644
--- a/services/core/java/com/android/server/display/WifiDisplayController.java
+++ b/services/core/java/com/android/server/display/WifiDisplayController.java
@@ -291,7 +291,7 @@
mWfdEnabling = true;
WifiP2pWfdInfo wfdInfo = new WifiP2pWfdInfo();
- wfdInfo.setWfdEnabled(true);
+ wfdInfo.setEnabled(true);
wfdInfo.setDeviceType(WifiP2pWfdInfo.DEVICE_TYPE_WFD_SOURCE);
wfdInfo.setSessionAvailable(true);
wfdInfo.setControlPort(DEFAULT_CONTROL_PORT);
@@ -323,7 +323,7 @@
// WFD should be disabled.
if (mWfdEnabled || mWfdEnabling) {
WifiP2pWfdInfo wfdInfo = new WifiP2pWfdInfo();
- wfdInfo.setWfdEnabled(false);
+ wfdInfo.setEnabled(false);
mWifiP2pManager.setWfdInfo(mWifiP2pChannel, wfdInfo, new ActionListener() {
@Override
public void onSuccess() {
@@ -1044,7 +1044,7 @@
private static boolean isWifiDisplay(WifiP2pDevice device) {
WifiP2pWfdInfo wfdInfo = device.getWfdInfo();
return wfdInfo != null
- && wfdInfo.isWfdEnabled()
+ && wfdInfo.isEnabled()
&& isPrimarySinkDeviceType(wfdInfo.getDeviceType());
}
diff --git a/services/core/java/com/android/server/media/BluetoothRouteProvider.java b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
index 669f1ac..6474303 100644
--- a/services/core/java/com/android/server/media/BluetoothRouteProvider.java
+++ b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
@@ -27,6 +27,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.media.AudioManager;
import android.media.MediaRoute2Info;
import android.text.TextUtils;
import android.util.Slog;
@@ -55,6 +56,7 @@
private final Context mContext;
private final BluetoothAdapter mBluetoothAdapter;
private final BluetoothRoutesUpdatedListener mListener;
+ private final AudioManager mAudioManager;
private final Map<String, BluetoothEventReceiver> mEventReceiverMap = new HashMap<>();
private final IntentFilter mIntentFilter = new IntentFilter();
private final BroadcastReceiver mBroadcastReceiver = new BluetoothBroadcastReceiver();
@@ -82,6 +84,7 @@
mContext = context;
mBluetoothAdapter = btAdapter;
mListener = listener;
+ mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
buildBluetoothRoutes();
mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.A2DP);
@@ -181,12 +184,15 @@
private BluetoothRouteInfo createBluetoothRoute(BluetoothDevice device) {
BluetoothRouteInfo newBtRoute = new BluetoothRouteInfo();
newBtRoute.btDevice = device;
+ // Current / Max volume will be set when connected.
+ // TODO: Is there any BT device which has fixed volume?
newBtRoute.route = new MediaRoute2Info.Builder(device.getAddress(), device.getName())
.addFeature(MediaRoute2Info.FEATURE_LIVE_AUDIO)
.setConnectionState(MediaRoute2Info.CONNECTION_STATE_DISCONNECTED)
.setDescription(mContext.getResources().getText(
R.string.bluetooth_a2dp_audio_route_name).toString())
.setDeviceType(MediaRoute2Info.DEVICE_TYPE_BLUETOOTH)
+ .setVolumeHandling(MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE)
.build();
newBtRoute.connectedProfiles = new SparseBooleanArray();
return newBtRoute;
@@ -203,10 +209,20 @@
Slog.w(TAG, "setRouteConnectionStateForDevice: route shouldn't be null");
return;
}
- if (btRoute.route.getConnectionState() != state) {
- btRoute.route = new MediaRoute2Info.Builder(btRoute.route)
- .setConnectionState(state).build();
+ if (btRoute.route.getConnectionState() == state) {
+ return;
}
+
+ // Update volume when the connection state is changed.
+ MediaRoute2Info.Builder builder = new MediaRoute2Info.Builder(btRoute.route)
+ .setConnectionState(state);
+
+ if (state == MediaRoute2Info.CONNECTION_STATE_CONNECTED) {
+ int maxVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
+ int currentVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
+ builder.setVolumeMax(maxVolume).setVolume(currentVolume);
+ }
+ btRoute.route = builder.build();
}
interface BluetoothRoutesUpdatedListener {
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 888f7ce..b5dcea8 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -20,9 +20,11 @@
import static android.media.MediaRoute2Info.FEATURE_LIVE_VIDEO;
import android.annotation.NonNull;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.media.AudioManager;
import android.media.AudioRoutesInfo;
import android.media.IAudioRoutesObserver;
@@ -107,6 +109,9 @@
}
});
initializeSessionInfo();
+
+ mContext.registerReceiver(new VolumeChangeReceiver(),
+ new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION));
}
@Override
@@ -278,4 +283,44 @@
}
mCallback.onSessionUpdated(this, sessionInfo);
}
+
+ private class VolumeChangeReceiver extends BroadcastReceiver {
+ // This will be called in the main thread.
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (!intent.getAction().equals(AudioManager.VOLUME_CHANGED_ACTION)) {
+ return;
+ }
+
+ final int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
+ if (streamType != AudioManager.STREAM_MUSIC) {
+ return;
+ }
+
+ final int newVolume = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0);
+ final int oldVolume = intent.getIntExtra(
+ AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, 0);
+
+ if (newVolume != oldVolume) {
+ String activeBtDeviceAddress = mBtRouteProvider.getActiveDeviceAddress();
+ if (!TextUtils.isEmpty(activeBtDeviceAddress)) {
+ for (int i = mBluetoothRoutes.size() - 1; i >= 0; i--) {
+ MediaRoute2Info route = mBluetoothRoutes.get(i);
+ if (TextUtils.equals(activeBtDeviceAddress, route.getId())) {
+ mBluetoothRoutes.set(i,
+ new MediaRoute2Info.Builder(route)
+ .setVolume(newVolume)
+ .build());
+ break;
+ }
+ }
+ } else {
+ mDefaultRoute = new MediaRoute2Info.Builder(mDefaultRoute)
+ .setVolume(newVolume)
+ .build();
+ }
+ publishRoutes();
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 87b04b2..c7d00ec 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -178,7 +178,7 @@
private long mLastActivityFinishTime;
// Last configuration that was reported to the process.
- private final Configuration mLastReportedConfiguration;
+ private final Configuration mLastReportedConfiguration = new Configuration();
// Configuration that is waiting to be dispatched to the process.
private Configuration mPendingConfiguration;
private final Configuration mNewOverrideConfig = new Configuration();
@@ -192,7 +192,7 @@
/** Whether our process is currently running a {@link IRemoteAnimationRunner} */
private boolean mRunningRemoteAnimation;
- public WindowProcessController(ActivityTaskManagerService atm, ApplicationInfo info,
+ public WindowProcessController(@NonNull ActivityTaskManagerService atm, ApplicationInfo info,
String name, int uid, int userId, Object owner, WindowProcessListener listener) {
mInfo = info;
mName = name;
@@ -201,11 +201,8 @@
mOwner = owner;
mListener = listener;
mAtm = atm;
- mLastReportedConfiguration = new Configuration();
mDisplayId = INVALID_DISPLAY;
- if (atm != null) {
- onConfigurationChanged(atm.getGlobalConfiguration());
- }
+ onConfigurationChanged(atm.getGlobalConfiguration());
}
public void setPid(int pid) {
@@ -220,6 +217,11 @@
public void setThread(IApplicationThread thread) {
synchronized (mAtm.mGlobalLockWithoutBoost) {
mThread = thread;
+ // In general this is called from attaching application, so the last configuration
+ // has been sent to client by {@link android.app.IApplicationThread#bindApplication}.
+ // If this process is system server, it is fine because system is booting and a new
+ // configuration will update when display is ready.
+ setLastReportedConfiguration(getConfiguration());
}
}
@@ -1060,7 +1062,6 @@
mNewOverrideConfig.setTo(mergedOverrideConfig);
mNewOverrideConfig.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED);
super.onRequestedOverrideConfigurationChanged(mNewOverrideConfig);
- updateConfiguration();
}
private void updateConfiguration() {
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
index 36c7213..d479892 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
@@ -17,6 +17,7 @@
package android.net.wifi.p2p;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
@@ -133,6 +134,7 @@
*
* By default this field is set to {@link #GROUP_OWNER_INTENT_AUTO}.
*/
+ @IntRange(from = 0, to = 15)
public int groupOwnerIntent = GROUP_OWNER_INTENT_AUTO;
/** @hide */
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pGroupList.java b/wifi/java/android/net/wifi/p2p/WifiP2pGroupList.java
index cdb2806..8a86311 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pGroupList.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pGroupList.java
@@ -22,7 +22,9 @@
import android.os.Parcelable;
import android.util.LruCache;
+import java.util.ArrayList;
import java.util.Collection;
+import java.util.List;
import java.util.Map;
@@ -78,8 +80,8 @@
* Get the list of P2P groups.
*/
@NonNull
- public Collection<WifiP2pGroup> getGroupList() {
- return mGroups.snapshot().values();
+ public List<WifiP2pGroup> getGroupList() {
+ return new ArrayList<>(mGroups.snapshot().values());
}
/**
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
index 0fe0675..9c2cad9 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
@@ -326,6 +326,12 @@
/**
* Broadcast intent action indicating that remembered persistent groups have changed.
+ *
+ * You can <em>not</em> receive this through components declared
+ * in manifests, only by explicitly registering for it with
+ * {@link android.content.Context#registerReceiver(android.content.BroadcastReceiver,
+ * android.content.IntentFilter) Context.registerReceiver()}.
+ *
* @hide
*/
@SystemApi
@@ -1347,20 +1353,33 @@
}
/**
- * Force p2p to enter or exit listen state
+ * Force p2p to enter listen state
*
* @param c is the channel created at {@link #initialize(Context, Looper, ChannelListener)}
- * @param enable enables or disables listening
* @param listener for callbacks on success or failure. Can be null.
*
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
- public void listen(@NonNull Channel c, boolean enable, @Nullable ActionListener listener) {
+ public void startListening(@NonNull Channel c, @Nullable ActionListener listener) {
checkChannel(c);
- c.mAsyncChannel.sendMessage(enable ? START_LISTEN : STOP_LISTEN,
- 0, c.putListener(listener));
+ c.mAsyncChannel.sendMessage(START_LISTEN, 0, c.putListener(listener));
+ }
+
+ /**
+ * Force p2p to exit listen state
+ *
+ * @param c is the channel created at {@link #initialize(Context, Looper, ChannelListener)}
+ * @param listener for callbacks on success or failure. Can be null.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+ public void stopListening(@NonNull Channel c, @Nullable ActionListener listener) {
+ checkChannel(c);
+ c.mAsyncChannel.sendMessage(STOP_LISTEN, 0, c.putListener(listener));
}
/**
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pWfdInfo.java b/wifi/java/android/net/wifi/p2p/WifiP2pWfdInfo.java
index 5484d24..e399b5b 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pWfdInfo.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pWfdInfo.java
@@ -17,6 +17,7 @@
package android.net.wifi.p2p;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
@@ -34,7 +35,7 @@
*/
public final class WifiP2pWfdInfo implements Parcelable {
- private boolean mWfdEnabled;
+ private boolean mEnabled;
/** Device information bitmap */
private int mDeviceInfo;
@@ -85,15 +86,15 @@
/** @hide */
@UnsupportedAppUsage
public WifiP2pWfdInfo(int devInfo, int ctrlPort, int maxTput) {
- mWfdEnabled = true;
+ mEnabled = true;
mDeviceInfo = devInfo;
mCtrlPort = ctrlPort;
mMaxThroughput = maxTput;
}
/** Returns true is Wifi Display is enabled, false otherwise. */
- public boolean isWfdEnabled() {
- return mWfdEnabled;
+ public boolean isEnabled() {
+ return mEnabled;
}
/**
@@ -101,8 +102,8 @@
*
* @param enabled true to enable Wifi Display, false to disable
*/
- public void setWfdEnabled(boolean enabled) {
- mWfdEnabled = enabled;
+ public void setEnabled(boolean enabled) {
+ mEnabled = enabled;
}
/**
@@ -177,12 +178,12 @@
}
/** Sets the TCP port at which the WFD Device listens for RTSP messages. */
- public void setControlPort(int port) {
+ public void setControlPort(@IntRange(from = 0) int port) {
mCtrlPort = port;
}
/** Sets the maximum average throughput capability of the WFD Device, in megabits/second. */
- public void setMaxThroughput(int maxThroughput) {
+ public void setMaxThroughput(@IntRange(from = 0) int maxThroughput) {
mMaxThroughput = maxThroughput;
}
@@ -200,7 +201,7 @@
@Override
public String toString() {
StringBuffer sbuf = new StringBuffer();
- sbuf.append("WFD enabled: ").append(mWfdEnabled);
+ sbuf.append("WFD enabled: ").append(mEnabled);
sbuf.append("WFD DeviceInfo: ").append(mDeviceInfo);
sbuf.append("\n WFD CtrlPort: ").append(mCtrlPort);
sbuf.append("\n WFD MaxThroughput: ").append(mMaxThroughput);
@@ -215,7 +216,7 @@
/** Copy constructor. */
public WifiP2pWfdInfo(@Nullable WifiP2pWfdInfo source) {
if (source != null) {
- mWfdEnabled = source.mWfdEnabled;
+ mEnabled = source.mEnabled;
mDeviceInfo = source.mDeviceInfo;
mCtrlPort = source.mCtrlPort;
mMaxThroughput = source.mMaxThroughput;
@@ -225,14 +226,14 @@
/** Implement the Parcelable interface */
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeInt(mWfdEnabled ? 1 : 0);
+ dest.writeInt(mEnabled ? 1 : 0);
dest.writeInt(mDeviceInfo);
dest.writeInt(mCtrlPort);
dest.writeInt(mMaxThroughput);
}
private void readFromParcel(Parcel in) {
- mWfdEnabled = (in.readInt() == 1);
+ mEnabled = (in.readInt() == 1);
mDeviceInfo = in.readInt();
mCtrlPort = in.readInt();
mMaxThroughput = in.readInt();
diff --git a/wifi/tests/src/android/net/wifi/p2p/WifiP2pDeviceTest.java b/wifi/tests/src/android/net/wifi/p2p/WifiP2pDeviceTest.java
index 17ee755..6edc287 100644
--- a/wifi/tests/src/android/net/wifi/p2p/WifiP2pDeviceTest.java
+++ b/wifi/tests/src/android/net/wifi/p2p/WifiP2pDeviceTest.java
@@ -45,7 +45,7 @@
assertEquals(devA.groupCapability, devB.groupCapability);
assertEquals(devA.status, devB.status);
if (devA.wfdInfo != null) {
- assertEquals(devA.wfdInfo.isWfdEnabled(), devB.wfdInfo.isWfdEnabled());
+ assertEquals(devA.wfdInfo.isEnabled(), devB.wfdInfo.isEnabled());
assertEquals(devA.wfdInfo.getDeviceInfoHex(), devB.wfdInfo.getDeviceInfoHex());
assertEquals(devA.wfdInfo.getControlPort(), devB.wfdInfo.getControlPort());
assertEquals(devA.wfdInfo.getMaxThroughput(), devB.wfdInfo.getMaxThroughput());
diff --git a/wifi/tests/src/android/net/wifi/p2p/WifiP2pWfdInfoTest.java b/wifi/tests/src/android/net/wifi/p2p/WifiP2pWfdInfoTest.java
index 15a0aac..2a9b36b 100644
--- a/wifi/tests/src/android/net/wifi/p2p/WifiP2pWfdInfoTest.java
+++ b/wifi/tests/src/android/net/wifi/p2p/WifiP2pWfdInfoTest.java
@@ -55,8 +55,8 @@
public void testSettersGetters() throws Exception {
WifiP2pWfdInfo info = new WifiP2pWfdInfo();
- info.setWfdEnabled(true);
- assertTrue(info.isWfdEnabled());
+ info.setEnabled(true);
+ assertTrue(info.isEnabled());
info.setDeviceType(WifiP2pWfdInfo.DEVICE_TYPE_WFD_SOURCE);
assertEquals(WifiP2pWfdInfo.DEVICE_TYPE_WFD_SOURCE, info.getDeviceType());