Merge changes Ie889f696,Ie3d7189b,Ibeceb4c4,I08055c7e,I7b01513b, ...
* changes:
playback device should handle the active source claiming when it exists.
Set local active port to HOME when oneTouchPlay is triggered. Update local active source at the same time.
Add ro.hdmi.property_hdmi_cec_never_assign_logical_addresses to skip the logical addresses that will not be assigned.
Fix routing logic on handling routing change/info.
Add routingChange and routingInformation handlers.
Add setStreamPath handlers and do input switching according to the new active path.
Add ActiveSouce handling logic to switch to ARC input when the new Active is not under the current device.
diff --git a/Android.bp b/Android.bp
index 74ac85e..c5f415d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -746,6 +746,7 @@
"android.hardware.radio-V1.3-java",
"android.hardware.radio-V1.4-java",
"android.hardware.usb.gadget-V1.0-java",
+ "networkstack-aidl-interfaces-java",
"netd_aidl_interface-java",
"devicepolicyprotosnano",
],
@@ -873,11 +874,16 @@
name: "networkstack-aidl-interfaces",
local_include_dir: "core/java",
srcs: [
+ "core/java/android/net/INetworkMonitor.aidl",
+ "core/java/android/net/INetworkMonitorCallbacks.aidl",
+ "core/java/android/net/IIpMemoryStore.aidl",
"core/java/android/net/INetworkStackConnector.aidl",
"core/java/android/net/INetworkStackStatusCallback.aidl",
+ "core/java/android/net/PrivateDnsConfigParcel.aidl",
"core/java/android/net/dhcp/DhcpServingParamsParcel.aidl",
"core/java/android/net/dhcp/IDhcpServer.aidl",
"core/java/android/net/dhcp/IDhcpServerCallbacks.aidl",
+ "core/java/android/net/ipmemorystore/**/*.aidl",
],
api_dir: "aidl/networkstack",
}
diff --git a/api/current.txt b/api/current.txt
index 70d254b..b954910 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -26076,6 +26076,27 @@
field public static final int URI_COLUMN_INDEX = 2; // 0x2
}
+ public final class Session2Command implements android.os.Parcelable {
+ ctor public Session2Command(int);
+ ctor public Session2Command(java.lang.String, android.os.Bundle);
+ method public int describeContents();
+ method public int getCommandCode();
+ method public java.lang.String getCustomCommand();
+ method public android.os.Bundle getExtras();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final int COMMAND_CODE_CUSTOM = 0; // 0x0
+ field public static final android.os.Parcelable.Creator<android.media.Session2Command> CREATOR;
+ field public static final int RESULT_ERROR_UNKNOWN_ERROR = -1; // 0xffffffff
+ field public static final int RESULT_INFO_SKIPPED = 1; // 0x1
+ field public static final int RESULT_SUCCESS = 0; // 0x0
+ }
+
+ public static final class Session2Command.Result {
+ ctor public Session2Command.Result(int, android.os.Bundle);
+ method public int getResultCode();
+ method public android.os.Bundle getResultData();
+ }
+
public class SoundPool {
ctor public deprecated SoundPool(int, int, int);
method public final void autoPause();
@@ -27121,6 +27142,21 @@
package android.media.session {
+ public final class ControllerCallbackLink implements android.os.Parcelable {
+ method public int describeContents();
+ method public android.os.IBinder getBinder();
+ method public void notifyEvent(java.lang.String, android.os.Bundle);
+ method public void notifyExtrasChanged(android.os.Bundle);
+ method public void notifyMetadataChanged(android.media.MediaMetadata);
+ method public void notifyPlaybackStateChanged(android.media.session.PlaybackState);
+ method public void notifyQueueChanged(java.util.List<android.media.session.MediaSession.QueueItem>);
+ method public void notifyQueueTitleChanged(java.lang.CharSequence);
+ method public void notifySessionDestroyed();
+ method public void notifyVolumeInfoChanged(android.media.session.MediaController.PlaybackInfo);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.media.session.ControllerCallbackLink> CREATOR;
+ }
+
public final class MediaController {
ctor public MediaController(android.content.Context, android.media.session.MediaSession.Token);
method public void adjustVolume(int, int);
@@ -27355,6 +27391,36 @@
method public android.media.session.PlaybackState.CustomAction.Builder setExtras(android.os.Bundle);
}
+ public final class SessionCallbackLink implements android.os.Parcelable {
+ method public int describeContents();
+ method public android.os.IBinder getBinder();
+ method public void notifyAdjustVolume(java.lang.String, int, int, android.media.session.ControllerCallbackLink, int);
+ method public void notifyCommand(java.lang.String, int, int, android.media.session.ControllerCallbackLink, java.lang.String, android.os.Bundle, android.os.ResultReceiver);
+ method public void notifyCustomAction(java.lang.String, int, int, android.media.session.ControllerCallbackLink, java.lang.String, android.os.Bundle);
+ method public void notifyFastForward(java.lang.String, int, int, android.media.session.ControllerCallbackLink);
+ method public void notifyMediaButton(java.lang.String, int, int, android.content.Intent, int, android.os.ResultReceiver);
+ method public void notifyMediaButtonFromController(java.lang.String, int, int, android.media.session.ControllerCallbackLink, android.content.Intent);
+ method public void notifyNext(java.lang.String, int, int, android.media.session.ControllerCallbackLink);
+ method public void notifyPause(java.lang.String, int, int, android.media.session.ControllerCallbackLink);
+ method public void notifyPlay(java.lang.String, int, int, android.media.session.ControllerCallbackLink);
+ method public void notifyPlayFromMediaId(java.lang.String, int, int, android.media.session.ControllerCallbackLink, java.lang.String, android.os.Bundle);
+ method public void notifyPlayFromSearch(java.lang.String, int, int, android.media.session.ControllerCallbackLink, java.lang.String, android.os.Bundle);
+ method public void notifyPlayFromUri(java.lang.String, int, int, android.media.session.ControllerCallbackLink, android.net.Uri, android.os.Bundle);
+ method public void notifyPrepare(java.lang.String, int, int, android.media.session.ControllerCallbackLink);
+ method public void notifyPrepareFromMediaId(java.lang.String, int, int, android.media.session.ControllerCallbackLink, java.lang.String, android.os.Bundle);
+ method public void notifyPrepareFromSearch(java.lang.String, int, int, android.media.session.ControllerCallbackLink, java.lang.String, android.os.Bundle);
+ method public void notifyPrepareFromUri(java.lang.String, int, int, android.media.session.ControllerCallbackLink, android.net.Uri, android.os.Bundle);
+ method public void notifyPrevious(java.lang.String, int, int, android.media.session.ControllerCallbackLink);
+ method public void notifyRate(java.lang.String, int, int, android.media.session.ControllerCallbackLink, android.media.Rating);
+ method public void notifyRewind(java.lang.String, int, int, android.media.session.ControllerCallbackLink);
+ method public void notifySeekTo(java.lang.String, int, int, android.media.session.ControllerCallbackLink, long);
+ method public void notifySetVolumeTo(java.lang.String, int, int, android.media.session.ControllerCallbackLink, int);
+ method public void notifySkipToTrack(java.lang.String, int, int, android.media.session.ControllerCallbackLink, long);
+ method public void notifyStop(java.lang.String, int, int, android.media.session.ControllerCallbackLink);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.media.session.SessionCallbackLink> CREATOR;
+ }
+
}
package android.media.tv {
@@ -29682,9 +29748,6 @@
method public void setReferenceCounted(boolean);
}
- public static abstract class WifiManager.NetworkSuggestionsStatusCode implements java.lang.annotation.Annotation {
- }
-
public class WifiManager.WifiLock {
method public void acquire();
method public boolean isHeld();
@@ -44911,6 +44974,7 @@
method public boolean isEnabled();
method public void startResolutionActivity(android.app.Activity, int, android.content.Intent, android.app.PendingIntent) throws android.content.IntentSender.SendIntentException;
method public void switchToSubscription(int, android.app.PendingIntent);
+ method public void updateSubscriptionNickname(int, java.lang.String, android.app.PendingIntent);
field public static final java.lang.String ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS = "android.telephony.euicc.action.MANAGE_EMBEDDED_SUBSCRIPTIONS";
field public static final java.lang.String ACTION_NOTIFY_CARRIER_SETUP_INCOMPLETE = "android.telephony.euicc.action.NOTIFY_CARRIER_SETUP_INCOMPLETE";
field public static final int EMBEDDED_SUBSCRIPTION_RESULT_ERROR = 2; // 0x2
@@ -49123,6 +49187,7 @@
method public float getAxisValue(int);
method public float getAxisValue(int, int);
method public int getButtonState();
+ method public int getClassification();
method public int getDeviceId();
method public long getDownTime();
method public int getEdgeFlags();
@@ -49274,6 +49339,9 @@
field public static final int BUTTON_STYLUS_PRIMARY = 32; // 0x20
field public static final int BUTTON_STYLUS_SECONDARY = 64; // 0x40
field public static final int BUTTON_TERTIARY = 4; // 0x4
+ field public static final int CLASSIFICATION_AMBIGUOUS_GESTURE = 1; // 0x1
+ field public static final int CLASSIFICATION_DEEP_PRESS = 2; // 0x2
+ field public static final int CLASSIFICATION_NONE = 0; // 0x0
field public static final android.os.Parcelable.Creator<android.view.MotionEvent> CREATOR;
field public static final int EDGE_BOTTOM = 2; // 0x2
field public static final int EDGE_LEFT = 4; // 0x4
diff --git a/api/system-current.txt b/api/system-current.txt
index 31077de..320a794 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3214,6 +3214,22 @@
package android.media.session {
+ public final class ControllerCallbackLink implements android.os.Parcelable {
+ ctor public ControllerCallbackLink(android.media.session.ControllerCallbackLink.CallbackStub);
+ }
+
+ public static abstract class ControllerCallbackLink.CallbackStub {
+ ctor public ControllerCallbackLink.CallbackStub();
+ method public void onEvent(java.lang.String, android.os.Bundle);
+ method public void onExtrasChanged(android.os.Bundle);
+ method public void onMetadataChanged(android.media.MediaMetadata);
+ method public void onPlaybackStateChanged(android.media.session.PlaybackState);
+ method public void onQueueChanged(java.util.List<android.media.session.MediaSession.QueueItem>);
+ method public void onQueueTitleChanged(java.lang.CharSequence);
+ method public void onSessionDestroyed();
+ method public void onVolumeInfoChanged(android.media.session.MediaController.PlaybackInfo);
+ }
+
public final class MediaSessionManager {
method public void setOnMediaKeyListener(android.media.session.MediaSessionManager.OnMediaKeyListener, android.os.Handler);
method public void setOnVolumeKeyLongPressListener(android.media.session.MediaSessionManager.OnVolumeKeyLongPressListener, android.os.Handler);
@@ -6384,6 +6400,7 @@
public class SubscriptionInfo implements android.os.Parcelable {
method public java.util.List<android.telephony.UiccAccessRule> getAccessRules();
method public int getCardId();
+ method public int getProfileClass();
}
public class SubscriptionManager {
@@ -6393,6 +6410,11 @@
method public void setDefaultDataSubId(int);
method public void setDefaultSmsSubId(int);
field public static final android.net.Uri ADVANCED_CALLING_ENABLED_CONTENT_URI;
+ field public static final int PROFILE_CLASS_DEFAULT = -1; // 0xffffffff
+ field public static final int PROFILE_CLASS_OPERATIONAL = 2; // 0x2
+ field public static final int PROFILE_CLASS_PROVISIONING = 1; // 0x1
+ field public static final int PROFILE_CLASS_TESTING = 0; // 0x0
+ field public static final int PROFILE_CLASS_UNSET = -1; // 0xffffffff
field public static final android.net.Uri VT_ENABLED_CONTENT_URI;
field public static final android.net.Uri WFC_ENABLED_CONTENT_URI;
field public static final android.net.Uri WFC_MODE_CONTENT_URI;
diff --git a/cmds/idmap2/idmap2/Scan.cpp b/cmds/idmap2/idmap2/Scan.cpp
index 4918747..4f88127 100644
--- a/cmds/idmap2/idmap2/Scan.cpp
+++ b/cmds/idmap2/idmap2/Scan.cpp
@@ -39,6 +39,17 @@
using android::idmap2::utils::FindFiles;
namespace {
+
+struct InputOverlay {
+ bool operator<(InputOverlay const& rhs) const {
+ return priority < rhs.priority || (priority == rhs.priority && apk_path < rhs.apk_path);
+ }
+
+ std::string apk_path; // NOLINT(misc-non-private-member-variables-in-classes)
+ std::string idmap_path; // NOLINT(misc-non-private-member-variables-in-classes)
+ int priority; // NOLINT(misc-non-private-member-variables-in-classes)
+};
+
std::unique_ptr<std::vector<std::string>> FindApkFiles(const std::vector<std::string>& dirs,
bool recursive, std::ostream& out_error) {
const auto predicate = [](unsigned char type, const std::string& path) -> bool {
@@ -58,6 +69,7 @@
}
return std::make_unique<std::vector<std::string>>(paths.cbegin(), paths.cend());
}
+
} // namespace
bool Scan(const std::vector<std::string>& args, std::ostream& out_error) {
@@ -87,7 +99,7 @@
return false;
}
- std::vector<std::string> interesting_apks;
+ std::vector<InputOverlay> interesting_apks;
for (const std::string& path : *apk_paths) {
std::unique_ptr<const ZipFile> zip = ZipFile::Open(path);
if (!zip) {
@@ -132,27 +144,29 @@
continue;
}
+ // Sort the static overlays in ascending priority order
+ std::string idmap_path = Idmap::CanonicalIdmapPathFor(output_directory, path);
+ InputOverlay input{path, idmap_path, priority};
interesting_apks.insert(
- std::lower_bound(interesting_apks.begin(), interesting_apks.end(), path), path);
+ std::lower_bound(interesting_apks.begin(), interesting_apks.end(), input), input);
}
std::stringstream stream;
- for (const auto& apk : interesting_apks) {
- const std::string idmap_path = Idmap::CanonicalIdmapPathFor(output_directory, apk);
+ for (const auto& overlay : interesting_apks) {
std::stringstream dev_null;
- if (!Verify(std::vector<std::string>({"--idmap-path", idmap_path}), dev_null) &&
+ if (!Verify(std::vector<std::string>({"--idmap-path", overlay.idmap_path}), dev_null) &&
!Create(std::vector<std::string>({
"--target-apk-path",
target_apk_path,
"--overlay-apk-path",
- apk,
+ overlay.apk_path,
"--idmap-path",
- idmap_path,
+ overlay.idmap_path,
}),
out_error)) {
return false;
}
- stream << idmap_path << std::endl;
+ stream << overlay.idmap_path << std::endl;
}
std::cout << stream.str();
diff --git a/cmds/idmap2/static-checks.sh b/cmds/idmap2/static-checks.sh
index 560ccb6..ad9830b 100755
--- a/cmds/idmap2/static-checks.sh
+++ b/cmds/idmap2/static-checks.sh
@@ -70,7 +70,15 @@
function _cpplint()
{
local cpplint="${ANDROID_BUILD_TOP}/tools/repohooks/tools/cpplint.py"
- $cpplint --quiet $cpp_files
+ local output="$($cpplint --quiet $cpp_files 2>&1 >/dev/null | grep -v \
+ -e 'Found C system header after C++ system header.' \
+ -e 'Unknown NOLINT error category: misc-non-private-member-variables-in-classes' \
+ )"
+ if [[ "$output" ]]; then
+ echo "$output"
+ return 1
+ fi
+ return 0
}
function _parse_args()
diff --git a/cmds/media/src/com/android/commands/media/Media.java b/cmds/media/src/com/android/commands/media/Media.java
index 6788f7d..d160b73 100644
--- a/cmds/media/src/com/android/commands/media/Media.java
+++ b/cmds/media/src/com/android/commands/media/Media.java
@@ -19,12 +19,12 @@
import android.app.ActivityManager;
import android.content.Context;
-import android.content.pm.ParceledListSlice;
import android.media.MediaMetadata;
+import android.media.session.ControllerCallbackLink;
import android.media.session.ISessionController;
-import android.media.session.ISessionControllerCallback;
import android.media.session.ISessionManager;
import android.media.session.MediaController.PlaybackInfo;
+import android.media.session.MediaSession.QueueItem;
import android.media.session.PlaybackState;
import android.os.Bundle;
import android.os.HandlerThread;
@@ -178,13 +178,7 @@
KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, InputDevice.SOURCE_KEYBOARD));
}
- class ControllerMonitor extends ISessionControllerCallback.Stub {
- private final ISessionController mController;
-
- public ControllerMonitor(ISessionController controller) {
- mController = controller;
- }
-
+ class ControllerCallbackStub extends ControllerCallbackLink.CallbackStub {
@Override
public void onSessionDestroyed() {
System.out.println("onSessionDestroyed. Enter q to quit.");
@@ -208,25 +202,35 @@
}
@Override
- public void onQueueChanged(ParceledListSlice queue) throws RemoteException {
+ public void onQueueChanged(List<QueueItem> queue) {
System.out.println("onQueueChanged, "
- + (queue == null ? "null queue" : " size=" + queue.getList().size()));
+ + (queue == null ? "null queue" : " size=" + queue.size()));
}
@Override
- public void onQueueTitleChanged(CharSequence title) throws RemoteException {
+ public void onQueueTitleChanged(CharSequence title) {
System.out.println("onQueueTitleChange " + title);
}
@Override
- public void onExtrasChanged(Bundle extras) throws RemoteException {
+ public void onExtrasChanged(Bundle extras) {
System.out.println("onExtrasChanged " + extras);
}
@Override
- public void onVolumeInfoChanged(PlaybackInfo info) throws RemoteException {
+ public void onVolumeInfoChanged(PlaybackInfo info) {
System.out.println("onVolumeInfoChanged " + info);
}
+ }
+
+ private class ControllerMonitor {
+ private final ISessionController mController;
+ private final ControllerCallbackLink mControllerCallbackLink;
+
+ ControllerMonitor(ISessionController controller) {
+ mController = controller;
+ mControllerCallbackLink = new ControllerCallbackLink(new ControllerCallbackStub());
+ }
void printUsageMessage() {
try {
@@ -244,7 +248,7 @@
@Override
protected void onLooperPrepared() {
try {
- mController.registerCallbackListener(PACKAGE_NAME, ControllerMonitor.this);
+ mController.registerCallbackListener(PACKAGE_NAME, mControllerCallbackLink);
} catch (RemoteException e) {
System.out.println("Error registering monitor callback");
}
@@ -287,7 +291,7 @@
} finally {
cbThread.getLooper().quit();
try {
- mController.unregisterCallbackListener(this);
+ mController.unregisterCallbackListener(mControllerCallbackLink);
} catch (Exception e) {
// ignoring
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 8faf08a..7767f04 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -271,6 +271,13 @@
@UnsupportedAppUsage
final H mH = new H();
final Executor mExecutor = new HandlerExecutor(mH);
+ /**
+ * Maps from activity token to local record of running activities in this process.
+ *
+ * This variable is readable if the code is running in activity thread or holding {@link
+ * #mResourcesManager}. It's only writable if the code is running in activity thread and holding
+ * {@link #mResourcesManager}.
+ */
@UnsupportedAppUsage
final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();
/** The activities to be truly destroyed (not include relaunch). */
@@ -434,6 +441,10 @@
Configuration newConfig;
Configuration createdConfig;
Configuration overrideConfig;
+ // Used to save the last reported configuration from server side so that activity
+ // configuration transactions can always use the latest configuration.
+ @GuardedBy("this")
+ private Configuration mPendingOverrideConfig;
// Used for consolidating configs before sending on to Activity.
private Configuration tmpConfig = new Configuration();
// Callback used for updating activity override config.
@@ -3064,7 +3075,12 @@
}
r.setState(ON_CREATE);
- mActivities.put(r.token, r);
+ // updatePendingActivityConfiguration() reads from mActivities to update
+ // ActivityClientRecord which runs in a different thread. Protect modifications to
+ // mActivities to avoid race.
+ synchronized (mResourcesManager) {
+ mActivities.put(r.token, r);
+ }
} catch (SuperNotCalledException e) {
throw e;
@@ -4639,7 +4655,12 @@
r.setState(ON_DESTROY);
}
schedulePurgeIdler();
- mActivities.remove(token);
+ // updatePendingActivityConfiguration() reads from mActivities to update
+ // ActivityClientRecord which runs in a different thread. Protect modifications to
+ // mActivities to avoid race.
+ synchronized (mResourcesManager) {
+ mActivities.remove(token);
+ }
StrictMode.decrementExpectedActivityCount(activityClass);
return r;
}
@@ -5382,6 +5403,26 @@
}
}
+ @Override
+ public void updatePendingActivityConfiguration(IBinder activityToken,
+ Configuration overrideConfig) {
+ final ActivityClientRecord r;
+ synchronized (mResourcesManager) {
+ r = mActivities.get(activityToken);
+ }
+
+ if (r == null) {
+ if (DEBUG_CONFIGURATION) {
+ Slog.w(TAG, "Not found target activity to update its pending config.");
+ }
+ return;
+ }
+
+ synchronized (r) {
+ r.mPendingOverrideConfig = overrideConfig;
+ }
+ }
+
/**
* Handle new activity configuration and/or move to a different display.
* @param activityToken Target activity token.
@@ -5401,6 +5442,24 @@
final boolean movedToDifferentDisplay = displayId != INVALID_DISPLAY
&& displayId != r.activity.getDisplayId();
+ synchronized (r) {
+ if (r.mPendingOverrideConfig != null
+ && !r.mPendingOverrideConfig.isOtherSeqNewer(overrideConfig)) {
+ overrideConfig = r.mPendingOverrideConfig;
+ }
+ r.mPendingOverrideConfig = null;
+ }
+
+ if (r.overrideConfig != null && !r.overrideConfig.isOtherSeqNewer(overrideConfig)
+ && !movedToDifferentDisplay) {
+ if (DEBUG_CONFIGURATION) {
+ Slog.v(TAG, "Activity already handled newer configuration so drop this"
+ + " transaction. overrideConfig=" + overrideConfig + " r.overrideConfig="
+ + r.overrideConfig);
+ }
+ return;
+ }
+
// Perform updates.
r.overrideConfig = overrideConfig;
final ViewRootImpl viewRoot = r.activity.mDecor != null
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index b5295d1..07dbb6b 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -124,6 +124,10 @@
/** Restart the activity after it was stopped. */
public abstract void performRestartActivity(IBinder token, boolean start);
+ /** Set pending activity configuration in case it will be updated by other transaction item. */
+ public abstract void updatePendingActivityConfiguration(IBinder activityToken,
+ Configuration overrideConfig);
+
/** Deliver activity (override) configuration change. */
public abstract void handleActivityConfigurationChanged(IBinder activityToken,
Configuration overrideConfig, int displayId);
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 9ddf4bd..83c6fac 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -95,8 +95,10 @@
import android.net.EthernetManager;
import android.net.IConnectivityManager;
import android.net.IEthernetManager;
+import android.net.IIpMemoryStore;
import android.net.IIpSecService;
import android.net.INetworkPolicyManager;
+import android.net.IpMemoryStore;
import android.net.IpSecManager;
import android.net.NetworkPolicyManager;
import android.net.NetworkScoreManager;
@@ -120,6 +122,7 @@
import android.nfc.NfcManager;
import android.os.BatteryManager;
import android.os.BatteryStats;
+import android.os.BugreportManager;
import android.os.Build;
import android.os.DeviceIdleManager;
import android.os.DropBoxManager;
@@ -127,6 +130,7 @@
import android.os.IBatteryPropertiesRegistrar;
import android.os.IBinder;
import android.os.IDeviceIdleController;
+import android.os.IDumpstate;
import android.os.IHardwarePropertiesManager;
import android.os.IPowerManager;
import android.os.IRecoverySystem;
@@ -321,10 +325,21 @@
registerService(Context.NETWORK_STACK_SERVICE, NetworkStack.class,
new StaticServiceFetcher<NetworkStack>() {
- @Override
- public NetworkStack createService() {
- return new NetworkStack();
- }});
+ @Override
+ public NetworkStack createService() {
+ return new NetworkStack();
+ }});
+
+ registerService(Context.IP_MEMORY_STORE_SERVICE, IpMemoryStore.class,
+ new CachedServiceFetcher<IpMemoryStore>() {
+ @Override
+ public IpMemoryStore createService(final ContextImpl ctx)
+ throws ServiceNotFoundException {
+ IBinder b = ServiceManager.getServiceOrThrow(
+ Context.IP_MEMORY_STORE_SERVICE);
+ IIpMemoryStore service = IIpMemoryStore.Stub.asInterface(b);
+ return new IpMemoryStore(ctx, service);
+ }});
registerService(Context.IPSEC_SERVICE, IpSecManager.class,
new CachedServiceFetcher<IpSecManager>() {
@@ -1069,6 +1084,16 @@
return new IncidentManager(ctx);
}});
+ registerService(Context.BUGREPORT_SERVICE, BugreportManager.class,
+ new CachedServiceFetcher<BugreportManager>() {
+ @Override
+ public BugreportManager createService(ContextImpl ctx)
+ throws ServiceNotFoundException {
+ IBinder b = ServiceManager.getServiceOrThrow(Context.BUGREPORT_SERVICE);
+ return new BugreportManager(ctx.getOuterContext(),
+ IDumpstate.Stub.asInterface(b));
+ }});
+
registerService(Context.AUTOFILL_MANAGER_SERVICE, AutofillManager.class,
new CachedServiceFetcher<AutofillManager>() {
@Override
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 7da67d9..9247486 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -6815,8 +6815,12 @@
}
/**
- * Returns the list of input methods permitted by the device or profiles
- * owners of the current user. (*Not* calling user, due to a limitation in InputMethodManager.)
+ * Returns the list of input methods permitted by the device or profiles owners.
+ *
+ * <p>On {@link android.os.Build.VERSION_CODES#Q} and later devices, this method returns the
+ * result for the calling user.</p>
+ *
+ * <p>On Android P and prior devices, this method returns the result for the current user.</p>
*
* <p>Null means all input methods are allowed, if a non-null list is returned
* it will contain the intersection of the permitted lists for any device or profile
diff --git a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
index a2b7d58..8ee9e53 100644
--- a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
+++ b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
@@ -36,6 +36,11 @@
private Configuration mConfiguration;
@Override
+ public void preExecute(android.app.ClientTransactionHandler client, IBinder token) {
+ client.updatePendingActivityConfiguration(token, mConfiguration);
+ }
+
+ @Override
public void execute(ClientTransactionHandler client, IBinder token,
PendingTransactionActions pendingActions) {
// TODO(lifecycler): detect if PIP or multi-window mode changed and report it here.
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index eb7be6f..fb20d96 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -337,6 +337,15 @@
public static final int BIND_ADJUST_BELOW_PERCEPTIBLE = 0x0100;
/**
+ * @hide Flag for {@link #bindService}: the service being bound to represents a
+ * protected system component, so must have association restrictions applied to it.
+ * That is, a system config must have one or more allow-association tags limiting
+ * which packages it can interact with. If it does not have any such association
+ * restrictions, a default empty set will be created.
+ */
+ public static final int BIND_RESTRICT_ASSOCIATIONS = 0x00200000;
+
+ /**
* @hide Flag for {@link #bindService}: allows binding to a service provided
* by an instant app. Note that the caller may not have access to the instant
* app providing the service which is a violation of the instant app sandbox.
@@ -3093,6 +3102,7 @@
VIBRATOR_SERVICE,
//@hide: STATUS_BAR_SERVICE,
CONNECTIVITY_SERVICE,
+ //@hide: IP_MEMORY_STORE_SERVICE,
IPSEC_SERVICE,
//@hide: UPDATE_LOCK_SERVICE,
//@hide: NETWORKMANAGEMENT_SERVICE,
@@ -3630,6 +3640,14 @@
/**
* Use with {@link #getSystemService(String)} to retrieve a
+ * {@link android.net.IpMemoryStore} to store and read information about
+ * known networks.
+ * @hide
+ */
+ public static final String IP_MEMORY_STORE_SERVICE = "ipmemorystore";
+
+ /**
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.net.IpSecManager} for encrypting Sockets or Networks with
* IPSec.
*
@@ -4427,6 +4445,16 @@
public static final String STATS_MANAGER = "stats";
/**
+ * Service to capture a bugreport.
+ * @see #getSystemService(String)
+ * @see android.os.BugreportManager
+ * @hide
+ */
+ // TODO: Expose API when the implementation is more complete.
+ // @SystemApi
+ public static final String BUGREPORT_SERVICE = "bugreport";
+
+ /**
* Use with {@link #getSystemService(String)} to retrieve a {@link
* android.content.om.OverlayManager} for managing overlay packages.
*
diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java
index a8c3b88..6e519c1 100644
--- a/core/java/android/content/pm/RegisteredServicesCache.java
+++ b/core/java/android/content/pm/RegisteredServicesCache.java
@@ -475,6 +475,14 @@
final List<ResolveInfo> resolveInfos = queryIntentServices(userId);
for (ResolveInfo resolveInfo : resolveInfos) {
try {
+ // if this package is not one of those changedUids, we don't need to scan it,
+ // since nothing in it changed, so save a call to parseServiceInfo, which
+ // can cause a large amount of the package apk to be loaded into memory.
+ // if this is the initial scan, changedUids will be null, and containsUid will
+ // trivially return true, and will call parseServiceInfo
+ if (!containsUid(changedUids, resolveInfo.serviceInfo.applicationInfo.uid)) {
+ continue;
+ }
ServiceInfo<V> info = parseServiceInfo(resolveInfo);
if (info == null) {
Log.w(TAG, "Unable to load service info " + resolveInfo.toString());
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 436b4a1..abc00fe 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -2051,6 +2051,16 @@
return (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
}
+ /** @hide */
+ public NetworkRequest getDefaultRequest() {
+ try {
+ // This is not racy as the default request is final in ConnectivityService.
+ return mService.getDefaultRequest();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/* TODO: These permissions checks don't belong in client-side code. Move them to
* services.jar, possibly in com.android.server.net. */
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index e7d441d..da5d96e 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -167,6 +167,8 @@
int getMultipathPreference(in Network Network);
+ NetworkRequest getDefaultRequest();
+
int getRestoreDefaultNetworkDelay(int networkType);
boolean addVpnAddress(String address, int prefixLength);
diff --git a/core/java/android/net/IIpMemoryStore.aidl b/core/java/android/net/IIpMemoryStore.aidl
new file mode 100644
index 0000000..6f88dec
--- /dev/null
+++ b/core/java/android/net/IIpMemoryStore.aidl
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.net.ipmemorystore.Blob;
+import android.net.ipmemorystore.NetworkAttributesParcelable;
+import android.net.ipmemorystore.IOnBlobRetrievedListener;
+import android.net.ipmemorystore.IOnL2KeyResponseListener;
+import android.net.ipmemorystore.IOnNetworkAttributesRetrieved;
+import android.net.ipmemorystore.IOnSameNetworkResponseListener;
+import android.net.ipmemorystore.IOnStatusListener;
+
+/** {@hide} */
+oneway interface IIpMemoryStore {
+ /**
+ * Store network attributes for a given L2 key.
+ * If L2Key is null, choose automatically from the attributes ; passing null is equivalent to
+ * calling findL2Key with the attributes and storing in the returned value.
+ *
+ * @param l2Key The L2 key for the L2 network. Clients that don't know or care about the L2
+ * key and only care about grouping can pass a unique ID here like the ones
+ * generated by {@code java.util.UUID.randomUUID()}, but keep in mind the low
+ * relevance of such a network will lead to it being evicted soon if it's not
+ * refreshed. Use findL2Key to try and find a similar L2Key to these attributes.
+ * @param attributes The attributes for this network.
+ * @param listener A listener that will be invoked to inform of the completion of this call,
+ * or null if the client is not interested in learning about success/failure.
+ * @return (through the listener) The L2 key. This is useful if the L2 key was not specified.
+ * If the call failed, the L2 key will be null.
+ */
+ void storeNetworkAttributes(String l2Key, in NetworkAttributesParcelable attributes,
+ IOnStatusListener listener);
+
+ /**
+ * Store a binary blob associated with an L2 key and a name.
+ *
+ * @param l2Key The L2 key for this network.
+ * @param clientId The ID of the client.
+ * @param name The name of this data.
+ * @param data The data to store.
+ * @param listener A listener to inform of the completion of this call, or null if the client
+ * is not interested in learning about success/failure.
+ * @return (through the listener) A status to indicate success or failure.
+ */
+ void storeBlob(String l2Key, String clientId, String name, in Blob data,
+ IOnStatusListener listener);
+
+ /**
+ * Returns the best L2 key associated with the attributes.
+ *
+ * This will find a record that would be in the same group as the passed attributes. This is
+ * useful to choose the key for storing a sample or private data when the L2 key is not known.
+ * If multiple records are group-close to these attributes, the closest match is returned.
+ * If multiple records have the same closeness, the one with the smaller (unicode codepoint
+ * order) L2 key is returned.
+ * If no record matches these attributes, null is returned.
+ *
+ * @param attributes The attributes of the network to find.
+ * @param listener The listener that will be invoked to return the answer.
+ * @return (through the listener) The L2 key if one matched, or null.
+ */
+ void findL2Key(in NetworkAttributesParcelable attributes, IOnL2KeyResponseListener listener);
+
+ /**
+ * Returns whether, to the best of the store's ability to tell, the two specified L2 keys point
+ * to the same L3 network. Group-closeness is used to determine this.
+ *
+ * @param l2Key1 The key for the first network.
+ * @param l2Key2 The key for the second network.
+ * @param listener The listener that will be invoked to return the answer.
+ * @return (through the listener) A SameL3NetworkResponse containing the answer and confidence.
+ */
+ void isSameNetwork(String l2Key1, String l2Key2, IOnSameNetworkResponseListener listener);
+
+ /**
+ * Retrieve the network attributes for a key.
+ * If no record is present for this key, this will return null attributes.
+ *
+ * @param l2Key The key of the network to query.
+ * @param listener The listener that will be invoked to return the answer.
+ * @return (through the listener) The network attributes and the L2 key associated with
+ * the query.
+ */
+ void retrieveNetworkAttributes(String l2Key, IOnNetworkAttributesRetrieved listener);
+
+ /**
+ * Retrieve previously stored private data.
+ * If no data was stored for this L2 key and name this will return null.
+ *
+ * @param l2Key The L2 key.
+ * @param clientId The id of the client that stored this data.
+ * @param name The name of the data.
+ * @param listener The listener that will be invoked to return the answer.
+ * @return (through the listener) The private data (or null), with the L2 key
+ * and the name of the data associated with the query.
+ */
+ void retrieveBlob(String l2Key, String clientId, String name,
+ IOnBlobRetrievedListener listener);
+}
diff --git a/core/java/android/net/INetworkMonitor.aidl b/core/java/android/net/INetworkMonitor.aidl
new file mode 100644
index 0000000..41f969a
--- /dev/null
+++ b/core/java/android/net/INetworkMonitor.aidl
@@ -0,0 +1,45 @@
+/**
+ * Copyright (c) 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing perNmissions and
+ * limitations under the License.
+ */
+package android.net;
+
+import android.net.PrivateDnsConfigParcel;
+
+/** @hide */
+oneway interface INetworkMonitor {
+ // After a network has been tested this result can be sent with EVENT_NETWORK_TESTED.
+ // The network should be used as a default internet connection. It was found to be:
+ // 1. a functioning network providing internet access, or
+ // 2. a captive portal and the user decided to use it as is.
+ const int NETWORK_TEST_RESULT_VALID = 0;
+
+ // After a network has been tested this result can be sent with EVENT_NETWORK_TESTED.
+ // The network should not be used as a default internet connection. It was found to be:
+ // 1. a captive portal and the user is prompted to sign-in, or
+ // 2. a captive portal and the user did not want to use it, or
+ // 3. a broken network (e.g. DNS failed, connect failed, HTTP request failed).
+ const int NETWORK_TEST_RESULT_INVALID = 1;
+
+ void start();
+ void launchCaptivePortalApp();
+ void forceReevaluation(int uid);
+ void notifyPrivateDnsChanged(in PrivateDnsConfigParcel config);
+ void notifyDnsResponse(int returnCode);
+ void notifySystemReady();
+ void notifyNetworkConnected();
+ void notifyNetworkDisconnected();
+ void notifyLinkPropertiesChanged();
+ void notifyNetworkCapabilitiesChanged();
+}
\ No newline at end of file
diff --git a/core/java/android/net/INetworkMonitorCallbacks.aidl b/core/java/android/net/INetworkMonitorCallbacks.aidl
new file mode 100644
index 0000000..0bc2575
--- /dev/null
+++ b/core/java/android/net/INetworkMonitorCallbacks.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.net.INetworkMonitor;
+import android.net.PrivateDnsConfigParcel;
+
+/** @hide */
+oneway interface INetworkMonitorCallbacks {
+ void onNetworkMonitorCreated(in INetworkMonitor networkMonitor);
+ void notifyNetworkTested(int testResult, @nullable String redirectUrl);
+ void notifyPrivateDnsConfigResolved(in PrivateDnsConfigParcel config);
+ void showProvisioningNotification(String action);
+ void hideProvisioningNotification();
+}
\ No newline at end of file
diff --git a/core/java/android/net/INetworkStackConnector.aidl b/core/java/android/net/INetworkStackConnector.aidl
index be0dc07..2df8ab7 100644
--- a/core/java/android/net/INetworkStackConnector.aidl
+++ b/core/java/android/net/INetworkStackConnector.aidl
@@ -15,6 +15,7 @@
*/
package android.net;
+import android.net.INetworkMonitorCallbacks;
import android.net.dhcp.DhcpServingParamsParcel;
import android.net.dhcp.IDhcpServerCallbacks;
@@ -22,4 +23,5 @@
oneway interface INetworkStackConnector {
void makeDhcpServer(in String ifName, in DhcpServingParamsParcel params,
in IDhcpServerCallbacks cb);
+ void makeNetworkMonitor(int netId, String name, in INetworkMonitorCallbacks cb);
}
\ No newline at end of file
diff --git a/core/java/android/net/IpMemoryStore.java b/core/java/android/net/IpMemoryStore.java
new file mode 100644
index 0000000..b35f097
--- /dev/null
+++ b/core/java/android/net/IpMemoryStore.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.net.ipmemorystore.Blob;
+import android.net.ipmemorystore.IOnBlobRetrievedListener;
+import android.net.ipmemorystore.IOnL2KeyResponseListener;
+import android.net.ipmemorystore.IOnNetworkAttributesRetrieved;
+import android.net.ipmemorystore.IOnSameNetworkResponseListener;
+import android.net.ipmemorystore.IOnStatusListener;
+import android.net.ipmemorystore.NetworkAttributes;
+import android.os.RemoteException;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * The interface for system components to access the IP memory store.
+ * @see com.android.server.net.ipmemorystore.IpMemoryStoreService
+ * @hide
+ */
+@SystemService(Context.IP_MEMORY_STORE_SERVICE)
+public class IpMemoryStore {
+ @NonNull final Context mContext;
+ @NonNull final IIpMemoryStore mService;
+
+ public IpMemoryStore(@NonNull final Context context, @NonNull final IIpMemoryStore service) {
+ mContext = Preconditions.checkNotNull(context, "missing context");
+ mService = Preconditions.checkNotNull(service, "missing IIpMemoryStore");
+ }
+
+ /**
+ * Store network attributes for a given L2 key.
+ * If L2Key is null, choose automatically from the attributes ; passing null is equivalent to
+ * calling findL2Key with the attributes and storing in the returned value.
+ *
+ * @param l2Key The L2 key for the L2 network. Clients that don't know or care about the L2
+ * key and only care about grouping can pass a unique ID here like the ones
+ * generated by {@code java.util.UUID.randomUUID()}, but keep in mind the low
+ * relevance of such a network will lead to it being evicted soon if it's not
+ * refreshed. Use findL2Key to try and find a similar L2Key to these attributes.
+ * @param attributes The attributes for this network.
+ * @param listener A listener that will be invoked to inform of the completion of this call,
+ * or null if the client is not interested in learning about success/failure.
+ * Through the listener, returns the L2 key. This is useful if the L2 key was not specified.
+ * If the call failed, the L2 key will be null.
+ */
+ public void storeNetworkAttributes(@NonNull final String l2Key,
+ @NonNull final NetworkAttributes attributes,
+ @Nullable final IOnStatusListener listener) {
+ try {
+ mService.storeNetworkAttributes(l2Key, attributes.toParcelable(), listener);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Store a binary blob associated with an L2 key and a name.
+ *
+ * @param l2Key The L2 key for this network.
+ * @param clientId The ID of the client.
+ * @param name The name of this data.
+ * @param data The data to store.
+ * @param listener A listener to inform of the completion of this call, or null if the client
+ * is not interested in learning about success/failure.
+ * Through the listener, returns a status to indicate success or failure.
+ */
+ public void storeBlob(@NonNull final String l2Key, @NonNull final String clientId,
+ @NonNull final String name, @NonNull final Blob data,
+ @Nullable final IOnStatusListener listener) {
+ try {
+ mService.storeBlob(l2Key, clientId, name, data, listener);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the best L2 key associated with the attributes.
+ *
+ * This will find a record that would be in the same group as the passed attributes. This is
+ * useful to choose the key for storing a sample or private data when the L2 key is not known.
+ * If multiple records are group-close to these attributes, the closest match is returned.
+ * If multiple records have the same closeness, the one with the smaller (unicode codepoint
+ * order) L2 key is returned.
+ * If no record matches these attributes, null is returned.
+ *
+ * @param attributes The attributes of the network to find.
+ * @param listener The listener that will be invoked to return the answer.
+ * Through the listener, returns the L2 key if one matched, or null.
+ */
+ public void findL2Key(@NonNull final NetworkAttributes attributes,
+ @NonNull final IOnL2KeyResponseListener listener) {
+ try {
+ mService.findL2Key(attributes.toParcelable(), listener);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns whether, to the best of the store's ability to tell, the two specified L2 keys point
+ * to the same L3 network. Group-closeness is used to determine this.
+ *
+ * @param l2Key1 The key for the first network.
+ * @param l2Key2 The key for the second network.
+ * @param listener The listener that will be invoked to return the answer.
+ * Through the listener, a SameL3NetworkResponse containing the answer and confidence.
+ */
+ public void isSameNetwork(@NonNull final String l2Key1, @NonNull final String l2Key2,
+ @NonNull final IOnSameNetworkResponseListener listener) {
+ try {
+ mService.isSameNetwork(l2Key1, l2Key2, listener);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Retrieve the network attributes for a key.
+ * If no record is present for this key, this will return null attributes.
+ *
+ * @param l2Key The key of the network to query.
+ * @param listener The listener that will be invoked to return the answer.
+ * Through the listener, returns the network attributes and the L2 key associated with
+ * the query.
+ */
+ public void retrieveNetworkAttributes(@NonNull final String l2Key,
+ @NonNull final IOnNetworkAttributesRetrieved listener) {
+ try {
+ mService.retrieveNetworkAttributes(l2Key, listener);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Retrieve previously stored private data.
+ * If no data was stored for this L2 key and name this will return null.
+ *
+ * @param l2Key The L2 key.
+ * @param clientId The id of the client that stored this data.
+ * @param name The name of the data.
+ * @param listener The listener that will be invoked to return the answer.
+ * Through the listener, returns the private data (or null), with the L2 key
+ * and the name of the data associated with the query.
+ */
+ public void retrieveBlob(@NonNull final String l2Key, @NonNull final String clientId,
+ @NonNull final String name, @NonNull final IOnBlobRetrievedListener listener) {
+ try {
+ mService.retrieveBlob(l2Key, clientId, name, listener);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+}
diff --git a/core/java/android/net/NetworkStack.java b/core/java/android/net/NetworkStack.java
index d4a0ec63..2eac6de 100644
--- a/core/java/android/net/NetworkStack.java
+++ b/core/java/android/net/NetworkStack.java
@@ -48,14 +48,16 @@
public class NetworkStack {
private static final String TAG = NetworkStack.class.getSimpleName();
+ public static final String NETWORKSTACK_PACKAGE_NAME = "com.android.mainline.networkstack";
+
@NonNull
@GuardedBy("mPendingNetStackRequests")
- private final ArrayList<NetworkStackRequest> mPendingNetStackRequests = new ArrayList<>();
+ private final ArrayList<NetworkStackCallback> mPendingNetStackRequests = new ArrayList<>();
@Nullable
@GuardedBy("mPendingNetStackRequests")
private INetworkStackConnector mConnector;
- private interface NetworkStackRequest {
+ private interface NetworkStackCallback {
void onNetworkStackConnected(INetworkStackConnector connector);
}
@@ -77,6 +79,21 @@
});
}
+ /**
+ * Create a NetworkMonitor.
+ *
+ * <p>The INetworkMonitor will be returned asynchronously through the provided callbacks.
+ */
+ public void makeNetworkMonitor(Network network, String name, INetworkMonitorCallbacks cb) {
+ requestConnector(connector -> {
+ try {
+ connector.makeNetworkMonitor(network.netId, name, cb);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ });
+ }
+
private class NetworkStackConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
@@ -96,14 +113,14 @@
ServiceManager.addService(Context.NETWORK_STACK_SERVICE, service, false /* allowIsolated */,
DUMP_FLAG_PRIORITY_HIGH | DUMP_FLAG_PRIORITY_NORMAL);
- final ArrayList<NetworkStackRequest> requests;
+ final ArrayList<NetworkStackCallback> requests;
synchronized (mPendingNetStackRequests) {
requests = new ArrayList<>(mPendingNetStackRequests);
mPendingNetStackRequests.clear();
mConnector = connector;
}
- for (NetworkStackRequest r : requests) {
+ for (NetworkStackCallback r : requests) {
r.onNetworkStackConnected(connector);
}
}
@@ -124,7 +141,8 @@
"com.android.server.NetworkStackService",
true /* initialize */,
context.getClassLoader());
- connector = (IBinder) service.getMethod("makeConnector").invoke(null);
+ connector = (IBinder) service.getMethod("makeConnector", Context.class)
+ .invoke(null, context);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
Slog.wtf(TAG, "Could not create network stack connector from NetworkStackService");
// TODO: crash/reboot system here ?
@@ -153,7 +171,7 @@
}
// TODO: use this method to obtain the connector when implementing network stack operations
- private void requestConnector(@NonNull NetworkStackRequest request) {
+ private void requestConnector(@NonNull NetworkStackCallback request) {
// TODO: PID check.
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
// Don't even attempt to obtain the connector and give a nice error message
diff --git a/core/java/android/net/PrivateDnsConfigParcel.aidl b/core/java/android/net/PrivateDnsConfigParcel.aidl
new file mode 100644
index 0000000..b52fce6
--- /dev/null
+++ b/core/java/android/net/PrivateDnsConfigParcel.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+parcelable PrivateDnsConfigParcel {
+ String hostname;
+ String[] ips;
+}
diff --git a/core/java/android/net/ipmemorystore/Blob.aidl b/core/java/android/net/ipmemorystore/Blob.aidl
new file mode 100644
index 0000000..9dbef11
--- /dev/null
+++ b/core/java/android/net/ipmemorystore/Blob.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.ipmemorystore;
+
+/**
+ * A blob of data opaque to the memory store. The client mutates this at its own risk,
+ * and it is strongly suggested to never do it at all and treat this as immutable.
+ * {@hide}
+ */
+parcelable Blob {
+ byte[] data;
+}
diff --git a/core/java/android/net/ipmemorystore/IOnBlobRetrievedListener.aidl b/core/java/android/net/ipmemorystore/IOnBlobRetrievedListener.aidl
new file mode 100644
index 0000000..4926feb
--- /dev/null
+++ b/core/java/android/net/ipmemorystore/IOnBlobRetrievedListener.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.ipmemorystore;
+
+import android.net.ipmemorystore.Blob;
+import android.net.ipmemorystore.StatusParcelable;
+
+/** {@hide} */
+oneway interface IOnBlobRetrievedListener {
+ /**
+ * Private data was retrieved for the L2 key and name specified.
+ * Note this does not return the client ID, as clients are expected to only ever use one ID.
+ */
+ void onBlobRetrieved(in StatusParcelable status, in String l2Key, in String name,
+ in Blob data);
+}
diff --git a/core/java/android/net/ipmemorystore/IOnL2KeyResponseListener.aidl b/core/java/android/net/ipmemorystore/IOnL2KeyResponseListener.aidl
new file mode 100644
index 0000000..dea0cc4
--- /dev/null
+++ b/core/java/android/net/ipmemorystore/IOnL2KeyResponseListener.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.ipmemorystore;
+
+import android.net.ipmemorystore.StatusParcelable;
+
+/** {@hide} */
+oneway interface IOnL2KeyResponseListener {
+ /**
+ * The operation completed with the specified L2 key.
+ */
+ void onL2KeyResponse(in StatusParcelable status, in String l2Key);
+}
diff --git a/core/java/android/net/ipmemorystore/IOnNetworkAttributesRetrieved.aidl b/core/java/android/net/ipmemorystore/IOnNetworkAttributesRetrieved.aidl
new file mode 100644
index 0000000..57f59a1
--- /dev/null
+++ b/core/java/android/net/ipmemorystore/IOnNetworkAttributesRetrieved.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.ipmemorystore;
+
+import android.net.ipmemorystore.NetworkAttributesParcelable;
+import android.net.ipmemorystore.StatusParcelable;
+
+/** {@hide} */
+oneway interface IOnNetworkAttributesRetrieved {
+ /**
+ * Network attributes were fetched for the specified L2 key. While the L2 key will never
+ * be null, the attributes may be if no data is stored about this L2 key.
+ */
+ void onL2KeyResponse(in StatusParcelable status, in String l2Key,
+ in NetworkAttributesParcelable attributes);
+}
diff --git a/core/java/android/net/ipmemorystore/IOnSameNetworkResponseListener.aidl b/core/java/android/net/ipmemorystore/IOnSameNetworkResponseListener.aidl
new file mode 100644
index 0000000..294bd3b
--- /dev/null
+++ b/core/java/android/net/ipmemorystore/IOnSameNetworkResponseListener.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.ipmemorystore;
+
+import android.net.ipmemorystore.SameL3NetworkResponseParcelable;
+import android.net.ipmemorystore.StatusParcelable;
+
+/** {@hide} */
+oneway interface IOnSameNetworkResponseListener {
+ /**
+ * The memory store has come up with the answer to a query that was sent.
+ */
+ void onSameNetworkResponse(in StatusParcelable status,
+ in SameL3NetworkResponseParcelable response);
+}
diff --git a/core/java/android/net/ipmemorystore/IOnStatusListener.aidl b/core/java/android/net/ipmemorystore/IOnStatusListener.aidl
new file mode 100644
index 0000000..5d07504
--- /dev/null
+++ b/core/java/android/net/ipmemorystore/IOnStatusListener.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.ipmemorystore;
+
+import android.net.ipmemorystore.StatusParcelable;
+
+/** {@hide} */
+oneway interface IOnStatusListener {
+ /**
+ * The operation has completed with the specified status.
+ */
+ void onComplete(in StatusParcelable status);
+}
diff --git a/core/java/android/net/ipmemorystore/NetworkAttributes.java b/core/java/android/net/ipmemorystore/NetworkAttributes.java
new file mode 100644
index 0000000..d7e5b27
--- /dev/null
+++ b/core/java/android/net/ipmemorystore/NetworkAttributes.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.ipmemorystore;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A POD object to represent attributes of a single L2 network entry.
+ * @hide
+ */
+public class NetworkAttributes {
+ private static final boolean DBG = true;
+
+ // The v4 address that was assigned to this device the last time it joined this network.
+ // This typically comes from DHCP but could be something else like static configuration.
+ // This does not apply to IPv6.
+ // TODO : add a list of v6 prefixes for the v6 case.
+ @Nullable
+ public final Inet4Address assignedV4Address;
+
+ // Optionally supplied by the client if it has an opinion on L3 network. For example, this
+ // could be a hash of the SSID + security type on WiFi.
+ @Nullable
+ public final String groupHint;
+
+ // The list of DNS server addresses.
+ @Nullable
+ public final List<InetAddress> dnsAddresses;
+
+ // The mtu on this network.
+ @Nullable
+ public final Integer mtu;
+
+ NetworkAttributes(
+ @Nullable final Inet4Address assignedV4Address,
+ @Nullable final String groupHint,
+ @Nullable final List<InetAddress> dnsAddresses,
+ @Nullable final Integer mtu) {
+ if (mtu != null && mtu < 0) throw new IllegalArgumentException("MTU can't be negative");
+ this.assignedV4Address = assignedV4Address;
+ this.groupHint = groupHint;
+ this.dnsAddresses = null == dnsAddresses ? null :
+ Collections.unmodifiableList(new ArrayList<>(dnsAddresses));
+ this.mtu = mtu;
+ }
+
+ @VisibleForTesting
+ public NetworkAttributes(@NonNull final NetworkAttributesParcelable parcelable) {
+ // The call to the other constructor must be the first statement of this constructor,
+ // so everything has to be inline
+ this((Inet4Address) getByAddressOrNull(parcelable.assignedV4Address),
+ parcelable.groupHint,
+ blobArrayToInetAddressList(parcelable.dnsAddresses),
+ parcelable.mtu >= 0 ? parcelable.mtu : null);
+ }
+
+ @Nullable
+ private static InetAddress getByAddressOrNull(@Nullable final byte[] address) {
+ try {
+ return InetAddress.getByAddress(address);
+ } catch (UnknownHostException e) {
+ return null;
+ }
+ }
+
+ @Nullable
+ private static List<InetAddress> blobArrayToInetAddressList(@Nullable final Blob[] blobs) {
+ if (null == blobs) return null;
+ final ArrayList<InetAddress> list = new ArrayList<>(blobs.length);
+ for (final Blob b : blobs) {
+ final InetAddress addr = getByAddressOrNull(b.data);
+ if (null != addr) list.add(addr);
+ }
+ return list;
+ }
+
+ @Nullable
+ private static Blob[] inetAddressListToBlobArray(@Nullable final List<InetAddress> addresses) {
+ if (null == addresses) return null;
+ final ArrayList<Blob> blobs = new ArrayList<>();
+ for (int i = 0; i < addresses.size(); ++i) {
+ final InetAddress addr = addresses.get(i);
+ if (null == addr) continue;
+ final Blob b = new Blob();
+ b.data = addr.getAddress();
+ blobs.add(b);
+ }
+ return blobs.toArray(new Blob[0]);
+ }
+
+ /** Converts this NetworkAttributes to a parcelable object */
+ @NonNull
+ public NetworkAttributesParcelable toParcelable() {
+ final NetworkAttributesParcelable parcelable = new NetworkAttributesParcelable();
+ parcelable.assignedV4Address =
+ (null == assignedV4Address) ? null : assignedV4Address.getAddress();
+ parcelable.groupHint = groupHint;
+ parcelable.dnsAddresses = inetAddressListToBlobArray(dnsAddresses);
+ parcelable.mtu = (null == mtu) ? -1 : mtu;
+ return parcelable;
+ }
+
+ /** @hide */
+ public static class Builder {
+ @Nullable
+ private Inet4Address mAssignedAddress;
+ @Nullable
+ private String mGroupHint;
+ @Nullable
+ private List<InetAddress> mDnsAddresses;
+ @Nullable
+ private Integer mMtu;
+
+ /**
+ * Set the assigned address.
+ * @param assignedV4Address The assigned address.
+ * @return This builder.
+ */
+ public Builder setAssignedV4Address(@Nullable final Inet4Address assignedV4Address) {
+ mAssignedAddress = assignedV4Address;
+ return this;
+ }
+
+ /**
+ * Set the group hint.
+ * @param groupHint The group hint.
+ * @return This builder.
+ */
+ public Builder setGroupHint(@Nullable final String groupHint) {
+ mGroupHint = groupHint;
+ return this;
+ }
+
+ /**
+ * Set the DNS addresses.
+ * @param dnsAddresses The DNS addresses.
+ * @return This builder.
+ */
+ public Builder setDnsAddresses(@Nullable final List<InetAddress> dnsAddresses) {
+ if (DBG && null != dnsAddresses) {
+ // Parceling code crashes if one of the addresses is null, therefore validate
+ // them when running in debug.
+ for (final InetAddress address : dnsAddresses) {
+ if (null == address) throw new IllegalArgumentException("Null DNS address");
+ }
+ }
+ this.mDnsAddresses = dnsAddresses;
+ return this;
+ }
+
+ /**
+ * Set the MTU.
+ * @param mtu The MTU.
+ * @return This builder.
+ */
+ public Builder setMtu(@Nullable final Integer mtu) {
+ if (null != mtu && mtu < 0) throw new IllegalArgumentException("MTU can't be negative");
+ mMtu = mtu;
+ return this;
+ }
+
+ /**
+ * Return the built NetworkAttributes object.
+ * @return The built NetworkAttributes object.
+ */
+ public NetworkAttributes build() {
+ return new NetworkAttributes(mAssignedAddress, mGroupHint, mDnsAddresses, mMtu);
+ }
+ }
+
+ @Override
+ public boolean equals(@Nullable final Object o) {
+ if (!(o instanceof NetworkAttributes)) return false;
+ final NetworkAttributes other = (NetworkAttributes) o;
+ return Objects.equals(assignedV4Address, other.assignedV4Address)
+ && Objects.equals(groupHint, other.groupHint)
+ && Objects.equals(dnsAddresses, other.dnsAddresses)
+ && Objects.equals(mtu, other.mtu);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(assignedV4Address, groupHint, dnsAddresses, mtu);
+ }
+}
diff --git a/core/java/android/net/ipmemorystore/NetworkAttributesParcelable.aidl b/core/java/android/net/ipmemorystore/NetworkAttributesParcelable.aidl
new file mode 100644
index 0000000..0894d72
--- /dev/null
+++ b/core/java/android/net/ipmemorystore/NetworkAttributesParcelable.aidl
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.ipmemorystore;
+
+// Blob[] is used to represent an array of byte[], as structured AIDL does not support arrays
+// of arrays.
+import android.net.ipmemorystore.Blob;
+
+/**
+ * An object to represent attributes of a single L2 network entry.
+ * See NetworkAttributes.java for a description of each field. The types used in this class
+ * are structured parcelable types instead of the richer types of the NetworkAttributes object,
+ * but they have the same purpose. The NetworkAttributes.java file also contains the code
+ * to convert the richer types to the parcelable types and back.
+ * @hide
+ */
+parcelable NetworkAttributesParcelable {
+ byte[] assignedV4Address;
+ String groupHint;
+ Blob[] dnsAddresses;
+ int mtu;
+}
diff --git a/core/java/android/net/ipmemorystore/SameL3NetworkResponse.java b/core/java/android/net/ipmemorystore/SameL3NetworkResponse.java
new file mode 100644
index 0000000..0cb37e9
--- /dev/null
+++ b/core/java/android/net/ipmemorystore/SameL3NetworkResponse.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.ipmemorystore;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * An object representing the answer to a query whether two given L2 networks represent the
+ * same L3 network. Parcels as a SameL3NetworkResponseParceled object.
+ * @hide
+ */
+public class SameL3NetworkResponse {
+ @IntDef(prefix = "NETWORK_",
+ value = {NETWORK_SAME, NETWORK_DIFFERENT, NETWORK_NEVER_CONNECTED})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface NetworkSameness {}
+
+ /**
+ * Both L2 networks represent the same L3 network.
+ */
+ public static final int NETWORK_SAME = 1;
+
+ /**
+ * The two L2 networks represent a different L3 network.
+ */
+ public static final int NETWORK_DIFFERENT = 2;
+
+ /**
+ * The device has never connected to at least one of these two L2 networks, or data
+ * has been wiped. Therefore the device has never seen the L3 network behind at least
+ * one of these two L2 networks, and can't evaluate whether it's the same as the other.
+ */
+ public static final int NETWORK_NEVER_CONNECTED = 3;
+
+ /**
+ * The first L2 key specified in the query.
+ */
+ @NonNull
+ public final String l2Key1;
+
+ /**
+ * The second L2 key specified in the query.
+ */
+ @NonNull
+ public final String l2Key2;
+
+ /**
+ * A confidence value indicating whether the two L2 networks represent the same L3 network.
+ *
+ * If both L2 networks were known, this value will be between 0.0 and 1.0, with 0.0
+ * representing complete confidence that the given L2 networks represent a different
+ * L3 network, and 1.0 representing complete confidence that the given L2 networks
+ * represent the same L3 network.
+ * If at least one of the L2 networks was not known, this value will be outside of the
+ * 0.0~1.0 range.
+ *
+ * Most apps should not be interested in this, and are encouraged to use the collapsing
+ * {@link #getNetworkSameness()} function below.
+ */
+ public final float confidence;
+
+ /**
+ * @return whether the two L2 networks represent the same L3 network. Either
+ * {@code NETWORK_SAME}, {@code NETWORK_DIFFERENT} or {@code NETWORK_NEVER_CONNECTED}.
+ */
+ @NetworkSameness
+ public final int getNetworkSameness() {
+ if (confidence > 1.0 || confidence < 0.0) return NETWORK_NEVER_CONNECTED;
+ return confidence > 0.5 ? NETWORK_SAME : NETWORK_DIFFERENT;
+ }
+
+ SameL3NetworkResponse(@NonNull final String l2Key1, @NonNull final String l2Key2,
+ final float confidence) {
+ this.l2Key1 = l2Key1;
+ this.l2Key2 = l2Key2;
+ this.confidence = confidence;
+ }
+
+ /** Builds a SameL3NetworkResponse from a parcelable object */
+ @VisibleForTesting
+ public SameL3NetworkResponse(@NonNull final SameL3NetworkResponseParcelable parceled) {
+ this(parceled.l2Key1, parceled.l2Key2, parceled.confidence);
+ }
+
+ /** Converts this SameL3NetworkResponse to a parcelable object */
+ @NonNull
+ public SameL3NetworkResponseParcelable toParcelable() {
+ final SameL3NetworkResponseParcelable parcelable = new SameL3NetworkResponseParcelable();
+ parcelable.l2Key1 = l2Key1;
+ parcelable.l2Key2 = l2Key2;
+ parcelable.confidence = confidence;
+ return parcelable;
+ }
+
+ // Note key1 and key2 have to match each other for this to return true. If
+ // key1 matches o.key2 and the other way around this returns false.
+ @Override
+ public boolean equals(@Nullable final Object o) {
+ if (!(o instanceof SameL3NetworkResponse)) return false;
+ final SameL3NetworkResponse other = (SameL3NetworkResponse) o;
+ return l2Key1.equals(other.l2Key1) && l2Key2.equals(other.l2Key2)
+ && confidence == other.confidence;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(l2Key1, l2Key2, confidence);
+ }
+}
diff --git a/core/java/android/net/ipmemorystore/SameL3NetworkResponseParcelable.aidl b/core/java/android/net/ipmemorystore/SameL3NetworkResponseParcelable.aidl
new file mode 100644
index 0000000..7196699
--- /dev/null
+++ b/core/java/android/net/ipmemorystore/SameL3NetworkResponseParcelable.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.ipmemorystore;
+
+/** {@hide} */
+parcelable SameL3NetworkResponseParcelable {
+ String l2Key1;
+ String l2Key2;
+ float confidence;
+}
diff --git a/core/java/android/net/ipmemorystore/Status.java b/core/java/android/net/ipmemorystore/Status.java
new file mode 100644
index 0000000..5b016ec
--- /dev/null
+++ b/core/java/android/net/ipmemorystore/Status.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.ipmemorystore;
+
+import android.annotation.NonNull;
+
+/**
+ * A parcelable status representing the result of an operation.
+ * Parcels as StatusParceled.
+ * @hide
+ */
+public class Status {
+ public static final int SUCCESS = 0;
+
+ public final int resultCode;
+
+ public Status(final int resultCode) {
+ this.resultCode = resultCode;
+ }
+
+ Status(@NonNull final StatusParcelable parcelable) {
+ this(parcelable.resultCode);
+ }
+
+ /** Converts this Status to a parcelable object */
+ @NonNull
+ public StatusParcelable toParcelable() {
+ final StatusParcelable parcelable = new StatusParcelable();
+ parcelable.resultCode = resultCode;
+ return parcelable;
+ }
+
+ public boolean isSuccess() {
+ return SUCCESS == resultCode;
+ }
+}
diff --git a/core/java/android/net/ipmemorystore/StatusParcelable.aidl b/core/java/android/net/ipmemorystore/StatusParcelable.aidl
new file mode 100644
index 0000000..fb36ef4
--- /dev/null
+++ b/core/java/android/net/ipmemorystore/StatusParcelable.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.ipmemorystore;
+
+/** {@hide} */
+parcelable StatusParcelable {
+ int resultCode;
+}
diff --git a/core/java/android/os/BugreportManager.java b/core/java/android/os/BugreportManager.java
new file mode 100644
index 0000000..1343d24
--- /dev/null
+++ b/core/java/android/os/BugreportManager.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.os.IBinder.DeathRecipient;
+
+import java.io.FileDescriptor;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Class that provides a privileged API to capture and consume bugreports.
+ *
+ * @hide
+ */
+// TODO: Expose API when the implementation is more complete.
+// @SystemApi
+@SystemService(Context.BUGREPORT_SERVICE)
+public class BugreportManager {
+ private final Context mContext;
+ private final IDumpstate mBinder;
+
+ /** @hide */
+ public BugreportManager(@NonNull Context context, IDumpstate binder) {
+ mContext = context;
+ mBinder = binder;
+ }
+
+ /**
+ * An interface describing the listener for bugreport progress and status.
+ */
+ public interface BugreportListener {
+ /**
+ * Called when there is a progress update.
+ * @param progress the progress in [0.0, 100.0]
+ */
+ void onProgress(float progress);
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "BUGREPORT_ERROR_" }, value = {
+ BUGREPORT_ERROR_INVALID_INPUT,
+ BUGREPORT_ERROR_RUNTIME
+ })
+
+ /** Possible error codes taking a bugreport can encounter */
+ @interface BugreportErrorCode {}
+
+ /** The input options were invalid */
+ int BUGREPORT_ERROR_INVALID_INPUT = 1;
+
+ /** A runtime error occured */
+ int BUGREPORT_ERROR_RUNTIME = 2;
+
+ /**
+ * Called when taking bugreport resulted in an error.
+ *
+ * @param errorCode the error that occurred. Possible values are
+ * {@code BUGREPORT_ERROR_INVALID_INPUT}, {@code BUGREPORT_ERROR_RUNTIME}.
+ */
+ void onError(@BugreportErrorCode int errorCode);
+
+ /**
+ * Called when taking bugreport finishes successfully
+ *
+ * @param durationMs time capturing bugreport took in milliseconds
+ * @param title title for the bugreport; helpful in reminding the user why they took it
+ * @param description detailed description for the bugreport
+ */
+ void onFinished(long durationMs, @NonNull String title,
+ @NonNull String description);
+ }
+
+ /**
+ * Starts a bugreport asynchronously.
+ *
+ * @param bugreportFd file to write the bugreport. This should be opened in write-only,
+ * append mode.
+ * @param screenshotFd file to write the screenshot, if necessary. This should be opened
+ * in write-only, append mode.
+ * @param params options that specify what kind of a bugreport should be taken
+ * @param listener callback for progress and status updates
+ */
+ @RequiresPermission(android.Manifest.permission.DUMP)
+ public void startBugreport(@NonNull FileDescriptor bugreportFd,
+ @Nullable FileDescriptor screenshotFd,
+ @NonNull BugreportParams params, @Nullable BugreportListener listener) {
+ // TODO(b/111441001): Enforce android.Manifest.permission.DUMP if necessary.
+ DumpstateListener dsListener = new DumpstateListener(listener);
+
+ try {
+ mBinder.startBugreport(bugreportFd, screenshotFd, params.getMode(), dsListener);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+
+ // TODO(b/111441001) Connect up with BugreportListener methods.
+ private final class DumpstateListener extends IDumpstateListener.Stub
+ implements DeathRecipient {
+ private final BugreportListener mListener;
+
+ DumpstateListener(@Nullable BugreportListener listener) {
+ mListener = listener;
+ }
+
+ @Override
+ public void binderDied() {
+ // TODO(b/111441001): implement
+ }
+
+ @Override
+ public void onProgressUpdated(int progress) throws RemoteException {
+ // TODO(b/111441001): implement
+ }
+
+ @Override
+ public void onMaxProgressUpdated(int maxProgress) throws RemoteException {
+ // TODO(b/111441001): implement
+ }
+
+ @Override
+ public void onSectionComplete(String title, int status, int size, int durationMs)
+ throws RemoteException {
+ // TODO(b/111441001): implement
+ }
+ }
+}
diff --git a/core/java/android/os/BugreportParams.java b/core/java/android/os/BugreportParams.java
new file mode 100644
index 0000000..4e696ae
--- /dev/null
+++ b/core/java/android/os/BugreportParams.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Parameters that specify what kind of bugreport should be taken.
+ *
+ * @hide
+ */
+// TODO: Expose API when the implementation is more complete.
+// @SystemApi
+public final class BugreportParams {
+ private final int mMode;
+
+ public BugreportParams(@BugreportMode int mode) {
+ mMode = mode;
+ }
+
+ public int getMode() {
+ return mMode;
+ }
+
+ /**
+ * Defines acceptable types of bugreports.
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "BUGREPORT_MODE_" }, value = {
+ BUGREPORT_MODE_FULL,
+ BUGREPORT_MODE_INTERACTIVE,
+ BUGREPORT_MODE_REMOTE,
+ BUGREPORT_MODE_WEAR,
+ BUGREPORT_MODE_TELEPHONY,
+ BUGREPORT_MODE_WIFI
+ })
+ public @interface BugreportMode {}
+
+ /**
+ * Options for a bugreport without user interference (and hence causing less
+ * interference to the system), but includes all sections.
+ */
+ public static final int BUGREPORT_MODE_FULL = IDumpstate.BUGREPORT_MODE_FULL;
+
+ /**
+ * Options that allow user to monitor progress and enter additional data; might not
+ * include all sections.
+ */
+ public static final int BUGREPORT_MODE_INTERACTIVE = IDumpstate.BUGREPORT_MODE_INTERACTIVE;
+
+ /**
+ * Options for a bugreport requested remotely by administrator of the Device Owner app,
+ * not the device's user.
+ */
+ public static final int BUGREPORT_MODE_REMOTE = IDumpstate.BUGREPORT_MODE_REMOTE;
+
+ /**
+ * Options for a bugreport on a wearable device.
+ */
+ public static final int BUGREPORT_MODE_WEAR = IDumpstate.BUGREPORT_MODE_WEAR;
+
+ /**
+ * Options for a lightweight version of bugreport that only includes a few, urgent
+ * sections used to report telephony bugs.
+ */
+ public static final int BUGREPORT_MODE_TELEPHONY = IDumpstate.BUGREPORT_MODE_TELEPHONY;
+
+ /**
+ * Options for a lightweight bugreport that only includes a few sections related to
+ * Wifi.
+ */
+ public static final int BUGREPORT_MODE_WIFI = IDumpstate.BUGREPORT_MODE_WIFI;
+}
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index b5aeba0..226d1d0 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -74,10 +74,13 @@
private final String mPackageName;
// The default vibration intensity level for haptic feedback.
@VibrationIntensity
- private final int mDefaultHapticFeedbackIntensity;
+ private int mDefaultHapticFeedbackIntensity;
// The default vibration intensity level for notifications.
@VibrationIntensity
- private final int mDefaultNotificationVibrationIntensity;
+ private int mDefaultNotificationVibrationIntensity;
+ // The default vibration intensity level for ringtones.
+ @VibrationIntensity
+ private int mDefaultRingVibrationIntensity;
/**
* @hide to prevent subclassing from outside of the framework
@@ -85,10 +88,7 @@
public Vibrator() {
mPackageName = ActivityThread.currentPackageName();
final Context ctx = ActivityThread.currentActivityThread().getSystemContext();
- mDefaultHapticFeedbackIntensity = loadDefaultIntensity(ctx,
- com.android.internal.R.integer.config_defaultHapticFeedbackIntensity);
- mDefaultNotificationVibrationIntensity = loadDefaultIntensity(ctx,
- com.android.internal.R.integer.config_defaultNotificationVibrationIntensity);
+ loadVibrationIntensities(ctx);
}
/**
@@ -96,10 +96,16 @@
*/
protected Vibrator(Context context) {
mPackageName = context.getOpPackageName();
+ loadVibrationIntensities(context);
+ }
+
+ private void loadVibrationIntensities(Context context) {
mDefaultHapticFeedbackIntensity = loadDefaultIntensity(context,
com.android.internal.R.integer.config_defaultHapticFeedbackIntensity);
mDefaultNotificationVibrationIntensity = loadDefaultIntensity(context,
com.android.internal.R.integer.config_defaultNotificationVibrationIntensity);
+ mDefaultRingVibrationIntensity = loadDefaultIntensity(context,
+ com.android.internal.R.integer.config_defaultRingVibrationIntensity);
}
private int loadDefaultIntensity(Context ctx, int resId) {
@@ -115,13 +121,20 @@
}
/**
- * Get the default vibration intensity for notifications and ringtones.
+ * Get the default vibration intensity for notifications.
* @hide
*/
public int getDefaultNotificationVibrationIntensity() {
return mDefaultNotificationVibrationIntensity;
}
+ /** Get the default vibration intensity for ringtones.
+ * @hide
+ */
+ public int getDefaultRingVibrationIntensity() {
+ return mDefaultRingVibrationIntensity;
+ }
+
/**
* Check whether the hardware has a vibrator.
*
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index a9b76c3..50b02b9 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -86,7 +86,7 @@
import android.util.ArraySet;
import android.util.Log;
import android.util.MemoryIntArray;
-import android.view.textservice.TextServicesManager;
+import android.view.inputmethod.InputMethodSystemProperty;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.ColorDisplayController;
@@ -3377,6 +3377,22 @@
*/
public static final String NOTIFICATION_VIBRATION_INTENSITY =
"notification_vibration_intensity";
+ /**
+ * The intensity of ringtone vibrations, if configurable.
+ *
+ * Not all devices are capable of changing their vibration intensity; on these devices
+ * there will likely be no difference between the various vibration intensities except for
+ * intensity 0 (off) and the rest.
+ *
+ * <b>Values:</b><br/>
+ * 0 - Vibration is disabled<br/>
+ * 1 - Weak vibrations<br/>
+ * 2 - Medium vibrations<br/>
+ * 3 - Strong vibrations
+ * @hide
+ */
+ public static final String RING_VIBRATION_INTENSITY =
+ "ring_vibration_intensity";
/**
* The intensity of haptic feedback vibrations, if configurable.
@@ -4246,6 +4262,7 @@
ACCELEROMETER_ROTATION,
SHOW_BATTERY_PERCENT,
NOTIFICATION_VIBRATION_INTENSITY,
+ RING_VIBRATION_INTENSITY,
HAPTIC_FEEDBACK_INTENSITY,
DISPLAY_COLOR_MODE,
ALARM_ALERT,
@@ -4397,6 +4414,7 @@
VALIDATORS.put(MUTE_STREAMS_AFFECTED, MUTE_STREAMS_AFFECTED_VALIDATOR);
VALIDATORS.put(VIBRATE_ON, VIBRATE_ON_VALIDATOR);
VALIDATORS.put(NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_VALIDATOR);
+ VALIDATORS.put(RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_VALIDATOR);
VALIDATORS.put(HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_VALIDATOR);
VALIDATORS.put(RINGTONE, RINGTONE_VALIDATOR);
VALIDATORS.put(NOTIFICATION_SOUND, NOTIFICATION_SOUND_VALIDATOR);
@@ -5998,6 +6016,15 @@
"lock_screen_allow_remote_input";
/**
+ * Indicates which clock face to show on lock screen and AOD.
+ * @hide
+ */
+ public static final String LOCK_SCREEN_CUSTOM_CLOCK_FACE = "lock_screen_custom_clock_face";
+
+ private static final Validator LOCK_SCREEN_CUSTOM_CLOCK_FACE_VALIDATOR =
+ ANY_STRING_VALIDATOR;
+
+ /**
* Set by the system to track if the user needs to see the call to action for
* the lockscreen notification policy.
* @hide
@@ -7814,6 +7841,19 @@
BOOLEAN_VALIDATOR;
/**
+ * Whether or not face unlock always requires user confirmation, meaning {@link
+ * android.hardware.biometrics.BiometricPrompt.Builder#setRequireConfirmation(boolean)}
+ * is always 'true'. This overrides the behavior that apps choose in the
+ * setRequireConfirmation API.
+ * @hide
+ */
+ public static final String FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION =
+ "face_unlock_always_require_confirmation";
+
+ private static final Validator FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION_VALIDATOR =
+ BOOLEAN_VALIDATOR;
+
+ /**
* Whether the assist gesture should be enabled.
*
* @hide
@@ -8410,6 +8450,7 @@
AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN,
FACE_UNLOCK_KEYGUARD_ENABLED,
FACE_UNLOCK_APP_ENABLED,
+ FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION,
ASSIST_GESTURE_ENABLED,
ASSIST_GESTURE_SILENCE_ALERTS_ENABLED,
ASSIST_GESTURE_WAKE_ENABLED,
@@ -8427,6 +8468,7 @@
HUSH_GESTURE_USED,
IN_CALL_NOTIFICATION_ENABLED,
LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
+ LOCK_SCREEN_CUSTOM_CLOCK_FACE,
LOCK_SCREEN_SHOW_NOTIFICATIONS,
ZEN_DURATION,
SHOW_ZEN_UPGRADE_NOTIFICATION,
@@ -8563,6 +8605,8 @@
AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN_VALIDATOR);
VALIDATORS.put(FACE_UNLOCK_KEYGUARD_ENABLED, FACE_UNLOCK_KEYGUARD_ENABLED_VALIDATOR);
VALIDATORS.put(FACE_UNLOCK_APP_ENABLED, FACE_UNLOCK_APP_ENABLED_VALIDATOR);
+ VALIDATORS.put(FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION,
+ FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION_VALIDATOR);
VALIDATORS.put(ASSIST_GESTURE_ENABLED, ASSIST_GESTURE_ENABLED_VALIDATOR);
VALIDATORS.put(ASSIST_GESTURE_SILENCE_ALERTS_ENABLED,
ASSIST_GESTURE_SILENCE_ALERTS_ENABLED_VALIDATOR);
@@ -8603,6 +8647,7 @@
VALIDATORS.put(ASSIST_GESTURE_SETUP_COMPLETE, BOOLEAN_VALIDATOR);
VALIDATORS.put(NOTIFICATION_NEW_INTERRUPTION_MODEL, BOOLEAN_VALIDATOR);
VALIDATORS.put(TRUST_AGENTS_EXTEND_UNLOCK, TRUST_AGENTS_EXTEND_UNLOCK_VALIDATOR);
+ VALIDATORS.put(LOCK_SCREEN_CUSTOM_CLOCK_FACE, LOCK_SCREEN_CUSTOM_CLOCK_FACE_VALIDATOR);
VALIDATORS.put(LOCK_SCREEN_WHEN_TRUST_LOST, LOCK_SCREEN_WHEN_TRUST_LOST_VALIDATOR);
}
@@ -8631,14 +8676,14 @@
CLONE_TO_MANAGED_PROFILE.add(ACCESSIBILITY_ENABLED);
CLONE_TO_MANAGED_PROFILE.add(ALLOW_MOCK_LOCATION);
CLONE_TO_MANAGED_PROFILE.add(ALLOWED_GEOLOCATION_ORIGINS);
- CLONE_TO_MANAGED_PROFILE.add(DEFAULT_INPUT_METHOD);
CLONE_TO_MANAGED_PROFILE.add(ENABLED_ACCESSIBILITY_SERVICES);
- CLONE_TO_MANAGED_PROFILE.add(ENABLED_INPUT_METHODS);
CLONE_TO_MANAGED_PROFILE.add(LOCATION_CHANGER);
CLONE_TO_MANAGED_PROFILE.add(LOCATION_MODE);
CLONE_TO_MANAGED_PROFILE.add(LOCATION_PROVIDERS_ALLOWED);
CLONE_TO_MANAGED_PROFILE.add(SELECTED_INPUT_METHOD_SUBTYPE);
- if (TextServicesManager.DISABLE_PER_PROFILE_SPELL_CHECKER) {
+ if (!InputMethodSystemProperty.PER_PROFILE_IME_ENABLED) {
+ CLONE_TO_MANAGED_PROFILE.add(DEFAULT_INPUT_METHOD);
+ CLONE_TO_MANAGED_PROFILE.add(ENABLED_INPUT_METHODS);
CLONE_TO_MANAGED_PROFILE.add(SELECTED_SPELL_CHECKER);
CLONE_TO_MANAGED_PROFILE.add(SELECTED_SPELL_CHECKER_SUBTYPE);
}
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 8b97e0e..0edcb3d 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -22,7 +22,9 @@
import android.text.TextUtils;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Map;
+import java.util.Set;
/**
* Util class to get feature flag information.
@@ -37,8 +39,11 @@
public static final String HEARING_AID_SETTINGS = "settings_bluetooth_hearing_aid";
public static final String SAFETY_HUB = "settings_safety_hub";
public static final String SCREENRECORD_LONG_PRESS = "settings_screenrecord_long_press";
+ public static final String AOD_IMAGEWALLPAPER_ENABLED = "settings_aod_imagewallpaper_enabled";
private static final Map<String, String> DEFAULT_FLAGS;
+ private static final Set<String> OBSERVABLE_FLAGS;
+
static {
DEFAULT_FLAGS = new HashMap<>();
DEFAULT_FLAGS.put("settings_audio_switcher", "true");
@@ -54,6 +59,10 @@
DEFAULT_FLAGS.put(HEARING_AID_SETTINGS, "false");
DEFAULT_FLAGS.put(SAFETY_HUB, "false");
DEFAULT_FLAGS.put(SCREENRECORD_LONG_PRESS, "false");
+ DEFAULT_FLAGS.put(AOD_IMAGEWALLPAPER_ENABLED, "false");
+
+ OBSERVABLE_FLAGS = new HashSet<>();
+ OBSERVABLE_FLAGS.add(AOD_IMAGEWALLPAPER_ENABLED);
}
/**
@@ -90,6 +99,16 @@
*/
public static void setEnabled(Context context, String feature, boolean enabled) {
SystemProperties.set(FFLAG_OVERRIDE_PREFIX + feature, enabled ? "true" : "false");
+
+ // Also update Settings.Global if needed so that we can observe it via observer.
+ if (OBSERVABLE_FLAGS.contains(feature)) {
+ setObservableFlag(context, feature, enabled);
+ }
+ }
+
+ private static void setObservableFlag(Context context, String feature, boolean enabled) {
+ Settings.Global.putString(
+ context.getContentResolver(), feature, enabled ? "true" : "false");
}
/**
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index a86abe5..9d3552f 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -18,6 +18,9 @@
import static android.view.Display.DEFAULT_DISPLAY;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.graphics.Matrix;
@@ -30,6 +33,7 @@
import dalvik.annotation.optimization.CriticalNative;
import dalvik.annotation.optimization.FastNative;
+import java.lang.annotation.Retention;
import java.util.Objects;
/**
@@ -462,7 +466,7 @@
/**
* This flag indicates that the event has been generated by a gesture generator. It
- * provides a hint to the GestureDector to not apply any touch slop.
+ * provides a hint to the GestureDetector to not apply any touch slop.
*
* @hide
*/
@@ -1391,6 +1395,42 @@
};
/**
+ * Classification constant: None.
+ *
+ * No additional information is available about the current motion event stream.
+ *
+ * @see #getClassification
+ */
+ public static final int CLASSIFICATION_NONE = 0;
+
+ /**
+ * Classification constant: Ambiguous gesture.
+ *
+ * The user's intent with respect to the current event stream is not yet determined.
+ * Gestural actions, such as scrolling, should be inhibited until the classification resolves
+ * to another value or the event stream ends.
+ *
+ * @see #getClassification
+ */
+ public static final int CLASSIFICATION_AMBIGUOUS_GESTURE = 1;
+
+ /**
+ * Classification constant: Deep press.
+ *
+ * The current event stream represents the user intentionally pressing harder on the screen.
+ * This classification type should be used to accelerate the long press behaviour.
+ *
+ * @see #getClassification
+ */
+ public static final int CLASSIFICATION_DEEP_PRESS = 2;
+
+ /** @hide */
+ @Retention(SOURCE)
+ @IntDef(prefix = { "CLASSIFICATION" }, value = {
+ CLASSIFICATION_NONE, CLASSIFICATION_AMBIGUOUS_GESTURE, CLASSIFICATION_DEEP_PRESS})
+ public @interface Classification {};
+
+ /**
* Tool type constant: Unknown tool type.
* This constant is used when the tool type is not known or is not relevant,
* such as for a trackball or other non-pointing device.
@@ -1478,7 +1518,7 @@
private static native long nativeInitialize(long nativePtr,
int deviceId, int source, int displayId, int action, int flags, int edgeFlags,
- int metaState, int buttonState,
+ int metaState, int buttonState, @Classification int classification,
float xOffset, float yOffset, float xPrecision, float yPrecision,
long downTimeNanos, long eventTimeNanos,
int pointerCount, PointerProperties[] pointerIds, PointerCoords[] pointerCoords);
@@ -1548,6 +1588,8 @@
@CriticalNative
private static native void nativeSetButtonState(long nativePtr, int buttonState);
@CriticalNative
+ private static native int nativeGetClassification(long nativePtr);
+ @CriticalNative
private static native int nativeGetActionButton(long nativePtr);
@CriticalNative
private static native void nativeSetActionButton(long nativePtr, int actionButton);
@@ -1648,7 +1690,7 @@
MotionEvent ev = obtain();
ev.mNativePtr = nativeInitialize(ev.mNativePtr,
deviceId, source, displayId, action, flags, edgeFlags, metaState, buttonState,
- 0, 0, xPrecision, yPrecision,
+ CLASSIFICATION_NONE, 0, 0, xPrecision, yPrecision,
downTime * NS_PER_MS, eventTime * NS_PER_MS,
pointerCount, pointerProperties, pointerCoords);
if (ev.mNativePtr == 0) {
@@ -1833,7 +1875,7 @@
ev.mNativePtr = nativeInitialize(ev.mNativePtr,
deviceId, source, displayId,
- action, 0, edgeFlags, metaState, 0,
+ action, 0, edgeFlags, metaState, 0 /*buttonState*/, CLASSIFICATION_NONE,
0, 0, xPrecision, yPrecision,
downTime * NS_PER_MS, eventTime * NS_PER_MS,
1, pp, pc);
@@ -2539,6 +2581,18 @@
}
/**
+ * Returns the classification for the current gesture.
+ * The classification may change as more events become available for the same gesture.
+ *
+ * @see #CLASSIFICATION_NONE
+ * @see #CLASSIFICATION_AMBIGUOUS_GESTURE
+ * @see #CLASSIFICATION_DEEP_PRESS
+ */
+ public @Classification int getClassification() {
+ return nativeGetClassification(mNativePtr);
+ }
+
+ /**
* Gets which button has been modified during a press or release action.
*
* For actions other than {@link #ACTION_BUTTON_PRESS} and {@link #ACTION_BUTTON_RELEASE}
@@ -3172,7 +3226,7 @@
/**
* Adds all of the movement samples of the specified event to this one if
* it is compatible. To be compatible, the event must have the same device id,
- * source, display id, action, flags, pointer count, pointer properties.
+ * source, display id, action, flags, classification, pointer count, pointer properties.
*
* Only applies to {@link #ACTION_MOVE} or {@link #ACTION_HOVER_MOVE} events.
*
@@ -3194,7 +3248,9 @@
if (nativeGetDeviceId(mNativePtr) != nativeGetDeviceId(event.mNativePtr)
|| nativeGetSource(mNativePtr) != nativeGetSource(event.mNativePtr)
|| nativeGetDisplayId(mNativePtr) != nativeGetDisplayId(event.mNativePtr)
- || nativeGetFlags(mNativePtr) != nativeGetFlags(event.mNativePtr)) {
+ || nativeGetFlags(mNativePtr) != nativeGetFlags(event.mNativePtr)
+ || nativeGetClassification(mNativePtr)
+ != nativeGetClassification(event.mNativePtr)) {
return false;
}
@@ -3282,7 +3338,7 @@
nativeGetDisplayId(mNativePtr),
nativeGetAction(mNativePtr), nativeGetFlags(mNativePtr),
nativeGetEdgeFlags(mNativePtr), nativeGetMetaState(mNativePtr),
- nativeGetButtonState(mNativePtr),
+ nativeGetButtonState(mNativePtr), nativeGetClassification(mNativePtr),
nativeGetXOffset(mNativePtr), nativeGetYOffset(mNativePtr),
nativeGetXPrecision(mNativePtr), nativeGetYPrecision(mNativePtr),
nativeGetDownTimeNanos(mNativePtr),
@@ -3376,7 +3432,7 @@
nativeGetDisplayId(mNativePtr),
newAction, nativeGetFlags(mNativePtr),
nativeGetEdgeFlags(mNativePtr), nativeGetMetaState(mNativePtr),
- nativeGetButtonState(mNativePtr),
+ nativeGetButtonState(mNativePtr), nativeGetClassification(mNativePtr),
nativeGetXOffset(mNativePtr), nativeGetYOffset(mNativePtr),
nativeGetXPrecision(mNativePtr), nativeGetYPrecision(mNativePtr),
nativeGetDownTimeNanos(mNativePtr), eventTimeNanos,
@@ -3409,6 +3465,8 @@
}
appendUnless("0", msg, ", buttonState=", MotionEvent.buttonStateToString(getButtonState()));
+ appendUnless(classificationToString(CLASSIFICATION_NONE), msg, ", classification=",
+ classificationToString(getClassification()));
appendUnless("0", msg, ", metaState=", KeyEvent.metaStateToString(getMetaState()));
appendUnless("0", msg, ", flags=0x", Integer.toHexString(getFlags()));
appendUnless("0", msg, ", edgeFlags=0x", Integer.toHexString(getEdgeFlags()));
@@ -3547,6 +3605,26 @@
}
/**
+ * Returns a string that represents the symbolic name of the specified classification.
+ *
+ * @param classification The classification type.
+ * @return The symbolic name of this classification.
+ * @hide
+ */
+ public static String classificationToString(@Classification int classification) {
+ switch (classification) {
+ case CLASSIFICATION_NONE:
+ return "NONE";
+ case CLASSIFICATION_AMBIGUOUS_GESTURE:
+ return "AMBIGUOUS_GESTURE";
+ case CLASSIFICATION_DEEP_PRESS:
+ return "DEEP_PRESS";
+
+ }
+ return "NONE";
+ }
+
+ /**
* Returns a string that represents the symbolic name of the specified tool type
* such as "TOOL_TYPE_FINGER" or an equivalent numeric constant such as "42" if unknown.
*
diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java
index 95a346f..c630177 100644
--- a/core/java/android/view/animation/Animation.java
+++ b/core/java/android/view/animation/Animation.java
@@ -794,7 +794,7 @@
* @deprecated All window animations are running with detached wallpaper.
*/
public boolean getDetachWallpaper() {
- return false;
+ return true;
}
/**
diff --git a/core/java/android/view/inputmethod/InputMethodSystemProperty.java b/core/java/android/view/inputmethod/InputMethodSystemProperty.java
index b233b75..57ed7f9 100644
--- a/core/java/android/view/inputmethod/InputMethodSystemProperty.java
+++ b/core/java/android/view/inputmethod/InputMethodSystemProperty.java
@@ -41,6 +41,17 @@
*/
private static final String PROP_DEBUG_MULTI_CLIENT_IME = "persist.debug.multi_client_ime";
+ /**
+ * System property key for debugging purpose. The value must be empty, "1", or "0".
+ *
+ * <p>Values 'y', 'yes', '1', 'true' or 'on' are considered true.</p>
+ *
+ * <p>To set, run "adb root && adb shell setprop persist.debug.per_profile_ime 1".</p>
+ *
+ * <p>This value will be ignored when {@link Build#IS_DEBUGGABLE} returns {@code false}.</p>
+ */
+ private static final String PROP_DEBUG_PER_PROFILE_IME = "persist.debug.per_profile_ime";
+
@Nullable
private static ComponentName getMultiClientImeComponentName() {
if (Build.IS_DEBUGGABLE) {
@@ -59,6 +70,9 @@
/**
* {@link ComponentName} of multi-client IME to be used.
*
+ * <p>TODO: Move this back to MultiClientInputMethodManagerService once
+ * {@link #PER_PROFILE_IME_ENABLED} always becomes {@code true}.</p>
+ *
* @hide
*/
@Nullable
@@ -68,7 +82,19 @@
/**
* {@code true} when multi-client IME is enabled.
*
+ * <p>TODO: Move this back to MultiClientInputMethodManagerService once
+ * {@link #PER_PROFILE_IME_ENABLED} always becomes {@code true}.</p>
+ *
* @hide
*/
public static final boolean MULTI_CLIENT_IME_ENABLED = (sMultiClientImeComponentName != null);
+
+ /**
+ * {@code true} when per-profile IME is enabled.
+ * @hide
+ */
+ public static final boolean PER_PROFILE_IME_ENABLED = MULTI_CLIENT_IME_ENABLED
+ || Build.IS_DEBUGGABLE && SystemProperties.getBoolean(
+ PROP_DEBUG_PER_PROFILE_IME, false);
+
}
diff --git a/core/java/android/view/textservice/TextServicesManager.java b/core/java/android/view/textservice/TextServicesManager.java
index 4c6862c..5dc8b19 100644
--- a/core/java/android/view/textservice/TextServicesManager.java
+++ b/core/java/android/view/textservice/TextServicesManager.java
@@ -67,12 +67,6 @@
private static final String TAG = TextServicesManager.class.getSimpleName();
private static final boolean DBG = false;
- /**
- * A compile time switch to control per-profile spell checker, which is not yet ready.
- * @hide
- */
- public static final boolean DISABLE_PER_PROFILE_SPELL_CHECKER = true;
-
private static TextServicesManager sInstance;
private final ITextServicesManager mService;
diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
index ecf8119..50cff5c 100644
--- a/core/jni/android_view_MotionEvent.cpp
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -334,7 +334,7 @@
static jlong android_view_MotionEvent_nativeInitialize(JNIEnv* env, jclass clazz,
jlong nativePtr,
jint deviceId, jint source, jint displayId, jint action, jint flags, jint edgeFlags,
- jint metaState, jint buttonState,
+ jint metaState, jint buttonState, jint classification,
jfloat xOffset, jfloat yOffset, jfloat xPrecision, jfloat yPrecision,
jlong downTimeNanos, jlong eventTimeNanos,
jint pointerCount, jobjectArray pointerPropertiesObjArray,
@@ -373,7 +373,8 @@
}
event->initialize(deviceId, source, displayId, action, 0, flags, edgeFlags, metaState,
- buttonState, xOffset, yOffset, xPrecision, yPrecision,
+ buttonState, static_cast<MotionClassification>(classification),
+ xOffset, yOffset, xPrecision, yPrecision,
downTimeNanos, eventTimeNanos, pointerCount, pointerProperties, rawPointerCoords);
return reinterpret_cast<jlong>(event);
@@ -668,6 +669,11 @@
event->setButtonState(buttonState);
}
+static jint android_view_MotionEvent_nativeGetClassification(jlong nativePtr) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ return static_cast<jint>(event->getClassification());
+}
+
static void android_view_MotionEvent_nativeOffsetLocation(jlong nativePtr, jfloat deltaX,
jfloat deltaY) {
MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
@@ -747,7 +753,7 @@
static const JNINativeMethod gMotionEventMethods[] = {
/* name, signature, funcPtr */
{ "nativeInitialize",
- "(JIIIIIIIIFFFFJJI[Landroid/view/MotionEvent$PointerProperties;"
+ "(JIIIIIIIIIFFFFJJI[Landroid/view/MotionEvent$PointerProperties;"
"[Landroid/view/MotionEvent$PointerCoords;)J",
(void*)android_view_MotionEvent_nativeInitialize },
{ "nativeDispose",
@@ -846,6 +852,9 @@
{ "nativeSetButtonState",
"(JI)V",
(void*)android_view_MotionEvent_nativeSetButtonState },
+ { "nativeGetClassification",
+ "(J)I",
+ (void*)android_view_MotionEvent_nativeGetClassification },
{ "nativeOffsetLocation",
"(JFF)V",
(void*)android_view_MotionEvent_nativeOffsetLocation },
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 140225e..a7e8dee 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1152,6 +1152,10 @@
Settings.System.NOTIFICATION_VIBRATION_INTENSITY more details on the constant values and
meanings. -->
<integer name="config_defaultNotificationVibrationIntensity">2</integer>
+ <!-- The default intensity level for ring vibrations. See
+ Settings.System.RING_VIBRATION_INTENSITY more details on the constant values and
+ meanings. -->
+ <integer name="config_defaultRingVibrationIntensity">2</integer>
<bool name="config_use_strict_phone_number_comparation">false</bool>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index f8c9037..f707ced 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3489,6 +3489,7 @@
<java-symbol type="integer" name="config_defaultHapticFeedbackIntensity" />
<java-symbol type="integer" name="config_defaultNotificationVibrationIntensity" />
+ <java-symbol type="integer" name="config_defaultRingVibrationIntensity" />
<java-symbol type="bool" name="config_maskMainBuiltInDisplayCutout" />
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index 1750dac..0f83a29 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -19,28 +19,35 @@
import static android.content.Intent.ACTION_EDIT;
import static android.content.Intent.ACTION_VIEW;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import android.app.Activity;
import android.app.ActivityThread;
import android.app.IApplicationThread;
+import android.app.servertransaction.ActivityConfigurationChangeItem;
import android.app.servertransaction.ActivityRelaunchItem;
import android.app.servertransaction.ClientTransaction;
import android.app.servertransaction.ClientTransactionItem;
import android.app.servertransaction.ResumeActivityItem;
import android.app.servertransaction.StopActivityItem;
import android.content.Intent;
+import android.content.res.Configuration;
import android.os.IBinder;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.MediumTest;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.util.MergedConfiguration;
+import android.view.Display;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.concurrent.CountDownLatch;
+
/**
* Test for verifying {@link android.app.ActivityThread} class.
* Build/Install/Run:
@@ -50,8 +57,12 @@
@MediumTest
public class ActivityThreadTest {
- private final ActivityTestRule mActivityTestRule =
- new ActivityTestRule(TestActivity.class, true /* initialTouchMode */,
+ // The first sequence number to try with. Use a large number to avoid conflicts with the first a
+ // few sequence numbers the framework used to launch the test activity.
+ private static final int BASE_SEQ = 10000;
+
+ private final ActivityTestRule<TestActivity> mActivityTestRule =
+ new ActivityTestRule<>(TestActivity.class, true /* initialTouchMode */,
false /* launchActivity */);
@Test
@@ -129,6 +140,179 @@
});
}
+ @Test
+ public void testHandleActivityConfigurationChanged() {
+ final TestActivity activity = mActivityTestRule.launchActivity(new Intent());
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ final int numOfConfig = activity.mNumOfConfigChanges;
+ applyConfigurationChange(activity, BASE_SEQ);
+ assertEquals(numOfConfig + 1, activity.mNumOfConfigChanges);
+ });
+ }
+
+ @Test
+ public void testHandleActivityConfigurationChanged_DropStaleConfigurations() {
+ final TestActivity activity = mActivityTestRule.launchActivity(new Intent());
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ // Set the sequence number to BASE_SEQ.
+ applyConfigurationChange(activity, BASE_SEQ);
+
+ final int orientation = activity.mConfig.orientation;
+ final int numOfConfig = activity.mNumOfConfigChanges;
+
+ // Try to apply an old configuration change.
+ applyConfigurationChange(activity, BASE_SEQ - 1);
+ assertEquals(numOfConfig, activity.mNumOfConfigChanges);
+ assertEquals(orientation, activity.mConfig.orientation);
+ });
+ }
+
+ @Test
+ public void testHandleActivityConfigurationChanged_ApplyNewConfigurations() {
+ final TestActivity activity = mActivityTestRule.launchActivity(new Intent());
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ // Set the sequence number to BASE_SEQ and record the final sequence number it used.
+ final int seq = applyConfigurationChange(activity, BASE_SEQ);
+
+ final int orientation = activity.mConfig.orientation;
+ final int numOfConfig = activity.mNumOfConfigChanges;
+
+ // Try to apply an new configuration change.
+ applyConfigurationChange(activity, seq + 1);
+ assertEquals(numOfConfig + 1, activity.mNumOfConfigChanges);
+ assertNotEquals(orientation, activity.mConfig.orientation);
+ });
+ }
+
+ @Test
+ public void testHandleActivityConfigurationChanged_PickNewerPendingConfiguration() {
+ final TestActivity activity = mActivityTestRule.launchActivity(new Intent());
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ // Set the sequence number to BASE_SEQ and record the final sequence number it used.
+ final int seq = applyConfigurationChange(activity, BASE_SEQ);
+
+ final int orientation = activity.mConfig.orientation;
+ final int numOfConfig = activity.mNumOfConfigChanges;
+
+ final ActivityThread activityThread = activity.getActivityThread();
+
+ final Configuration pendingConfig = new Configuration();
+ pendingConfig.orientation = orientation == Configuration.ORIENTATION_LANDSCAPE
+ ? Configuration.ORIENTATION_PORTRAIT
+ : Configuration.ORIENTATION_LANDSCAPE;
+ pendingConfig.seq = seq + 2;
+ activityThread.updatePendingActivityConfiguration(activity.getActivityToken(),
+ pendingConfig);
+
+ final Configuration newConfig = new Configuration();
+ newConfig.orientation = orientation;
+ newConfig.seq = seq + 1;
+
+ activityThread.handleActivityConfigurationChanged(activity.getActivityToken(),
+ newConfig, Display.INVALID_DISPLAY);
+ assertEquals(numOfConfig + 1, activity.mNumOfConfigChanges);
+ assertEquals(pendingConfig.orientation, activity.mConfig.orientation);
+ });
+ }
+
+ @Test
+ public void testHandleActivityConfigurationChanged_OnlyAppliesNewestConfiguration()
+ throws Exception {
+ final TestActivity activity = mActivityTestRule.launchActivity(new Intent());
+
+ final ActivityThread activityThread = activity.getActivityThread();
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ final Configuration config = new Configuration();
+ config.seq = BASE_SEQ;
+ config.orientation = Configuration.ORIENTATION_PORTRAIT;
+
+ activityThread.handleActivityConfigurationChanged(activity.getActivityToken(),
+ config, Display.INVALID_DISPLAY);
+ });
+
+ final int numOfConfig = activity.mNumOfConfigChanges;
+ final IApplicationThread appThread = activityThread.getApplicationThread();
+
+ activity.mConfigLatch = new CountDownLatch(1);
+ activity.mTestLatch = new CountDownLatch(1);
+
+ Configuration config = new Configuration();
+ config.seq = BASE_SEQ + 1;
+ config.smallestScreenWidthDp = 100;
+ appThread.scheduleTransaction(newActivityConfigTransaction(activity, config));
+
+ // Wait until the main thread is performing the configuration change for the configuration
+ // with sequence number BASE_SEQ + 1 before proceeding. This is to mimic the situation where
+ // the activity takes very long time to process configuration changes.
+ activity.mTestLatch.await();
+
+ config = new Configuration();
+ config.seq = BASE_SEQ + 2;
+ config.smallestScreenWidthDp = 200;
+ appThread.scheduleTransaction(newActivityConfigTransaction(activity, config));
+
+ config = new Configuration();
+ config.seq = BASE_SEQ + 3;
+ config.smallestScreenWidthDp = 300;
+ appThread.scheduleTransaction(newActivityConfigTransaction(activity, config));
+
+ config = new Configuration();
+ config.seq = BASE_SEQ + 4;
+ config.smallestScreenWidthDp = 400;
+ appThread.scheduleTransaction(newActivityConfigTransaction(activity, config));
+
+ activity.mConfigLatch.countDown();
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+ activity.mConfigLatch = null;
+ activity.mTestLatch = null;
+
+ // Only two more configuration changes: one with seq BASE_SEQ + 1; another with seq
+ // BASE_SEQ + 4. Configurations scheduled in between should be dropped.
+ assertEquals(numOfConfig + 2, activity.mNumOfConfigChanges);
+ assertEquals(400, activity.mConfig.smallestScreenWidthDp);
+ }
+
+ /**
+ * Calls {@link ActivityThread#handleActivityConfigurationChanged(IBinder, Configuration, int)}
+ * to try to push activity configuration to the activity for the given sequence number.
+ * <p>
+ * It uses orientation to push the configuration and it tries a different orientation if the
+ * first attempt doesn't make through, to rule out the possibility that the previous
+ * configuration already has the same orientation.
+ *
+ * @param activity the test target activity
+ * @param seq the specified sequence number
+ * @return the sequence number this method tried with the last time, so that the caller can use
+ * the next sequence number for next configuration update.
+ */
+ private int applyConfigurationChange(TestActivity activity, int seq) {
+ final ActivityThread activityThread = activity.getActivityThread();
+
+ final int numOfConfig = activity.mNumOfConfigChanges;
+ Configuration config = new Configuration();
+ config.orientation = Configuration.ORIENTATION_PORTRAIT;
+ config.seq = seq;
+ activityThread.handleActivityConfigurationChanged(activity.getActivityToken(), config,
+ Display.INVALID_DISPLAY);
+
+ if (activity.mNumOfConfigChanges > numOfConfig) {
+ return config.seq;
+ }
+
+ config = new Configuration();
+ config.orientation = Configuration.ORIENTATION_LANDSCAPE;
+ config.seq = seq + 1;
+ activityThread.handleActivityConfigurationChanged(activity.getActivityToken(), config,
+ Display.INVALID_DISPLAY);
+
+ return config.seq;
+ }
+
private static ClientTransaction newRelaunchResumeTransaction(Activity activity) {
final ClientTransactionItem callbackItem = ActivityRelaunchItem.obtain(null,
null, 0, new MergedConfiguration(), false /* preserveWindow */);
@@ -162,6 +346,16 @@
return transaction;
}
+ private static ClientTransaction newActivityConfigTransaction(Activity activity,
+ Configuration config) {
+ final ActivityConfigurationChangeItem item = ActivityConfigurationChangeItem.obtain(config);
+
+ final ClientTransaction transaction = newTransaction(activity);
+ transaction.addCallback(item);
+
+ return transaction;
+ }
+
private static ClientTransaction newTransaction(Activity activity) {
final IApplicationThread appThread = activity.getActivityThread().getApplicationThread();
return ClientTransaction.obtain(appThread, activity.getActivityToken());
@@ -169,5 +363,37 @@
// Test activity
public static class TestActivity extends Activity {
+ int mNumOfConfigChanges;
+ final Configuration mConfig = new Configuration();
+
+ /**
+ * A latch used to notify tests that we're about to wait for configuration latch. This
+ * is used to notify test code that preExecute phase for activity configuration change
+ * transaction has passed.
+ */
+ volatile CountDownLatch mTestLatch;
+ /**
+ * If not {@code null} {@link #onConfigurationChanged(Configuration)} won't return until the
+ * latch reaches 0.
+ */
+ volatile CountDownLatch mConfigLatch;
+
+ @Override
+ public void onConfigurationChanged(Configuration config) {
+ super.onConfigurationChanged(config);
+ mConfig.setTo(config);
+ ++mNumOfConfigChanges;
+
+ if (mConfigLatch != null) {
+ if (mTestLatch != null) {
+ mTestLatch.countDown();
+ }
+ try {
+ mConfigLatch.await();
+ } catch (InterruptedException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+ }
}
}
diff --git a/location/lib/Android.bp b/location/lib/Android.bp
index b09335c..35f2877 100644
--- a/location/lib/Android.bp
+++ b/location/lib/Android.bp
@@ -18,5 +18,7 @@
name: "com.android.location.provider",
srcs: ["java/**/*.java"],
api_packages: ["com.android.location.provider"],
- metalava_enabled: false,
+ srcs_lib: "framework",
+ srcs_lib_whitelist_dirs: ["location/java"],
+ srcs_lib_whitelist_pkgs: ["com.android.internal.location"],
}
diff --git a/media/java/android/media/Session2Command.java b/media/java/android/media/Session2Command.java
index d2a5aae..8b285f2 100644
--- a/media/java/android/media/Session2Command.java
+++ b/media/java/android/media/Session2Command.java
@@ -36,8 +36,6 @@
* Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
* <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
* for consistent behavior across all devices.
- * </p>
- * @hide
*/
public final class Session2Command implements Parcelable {
/**
@@ -151,14 +149,17 @@
}
@Override
- public void writeToParcel(Parcel dest, int flags) {
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ if (dest == null) {
+ throw new IllegalArgumentException("parcel shouldn't be null");
+ }
dest.writeInt(mCommandCode);
dest.writeString(mCustomCommand);
dest.writeBundle(mExtras);
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (!(obj instanceof Session2Command)) {
return false;
}
@@ -185,7 +186,7 @@
* @param resultCode result code
* @param resultData result data
*/
- public Result(int resultCode, Bundle resultData) {
+ public Result(int resultCode, @Nullable Bundle resultData) {
mResultCode = resultCode;
mResultData = resultData;
}
@@ -200,6 +201,7 @@
/**
* Returns the result data.
*/
+ @Nullable
public Bundle getResultData() {
return mResultData;
}
diff --git a/media/java/android/media/Session2CommandGroup.java b/media/java/android/media/Session2CommandGroup.java
index 122dfb1..4668ec4 100644
--- a/media/java/android/media/Session2CommandGroup.java
+++ b/media/java/android/media/Session2CommandGroup.java
@@ -130,7 +130,7 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
- dest.writeParcelableArray((Session2Command[]) mCommands.toArray(), 0);
+ dest.writeParcelableArray(mCommands.toArray(new Session2Command[0]), 0);
}
/**
diff --git a/media/java/android/media/Session2Token.java b/media/java/android/media/Session2Token.java
index 4634c69..e1fff38 100644
--- a/media/java/android/media/Session2Token.java
+++ b/media/java/android/media/Session2Token.java
@@ -171,7 +171,7 @@
dest.writeString(mPackageName);
dest.writeString(mServiceName);
// TODO: Uncomment below
- //dest.writeStrongBinder(mSessionLink.asBinder());
+ //dest.writeStrongBinder(mSessionLink.getBinder());
dest.writeString(mComponentName == null ? "" : mComponentName.flattenToString());
}
diff --git a/media/java/android/media/session/ControllerCallbackLink.aidl b/media/java/android/media/session/ControllerCallbackLink.aidl
new file mode 100644
index 0000000..8ee8c7d
--- /dev/null
+++ b/media/java/android/media/session/ControllerCallbackLink.aidl
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media.session;
+
+parcelable ControllerCallbackLink;
diff --git a/media/java/android/media/session/ControllerCallbackLink.java b/media/java/android/media/session/ControllerCallbackLink.java
new file mode 100644
index 0000000..a143c9b
--- /dev/null
+++ b/media/java/android/media/session/ControllerCallbackLink.java
@@ -0,0 +1,272 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.session;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.media.MediaMetadata;
+import android.media.session.MediaController.PlaybackInfo;
+import android.media.session.MediaSession.QueueItem;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+
+import java.util.List;
+
+/**
+ * Handles incoming commands to {@link MediaController.Callback}.
+ * <p>
+ * This API is not generally intended for third party application developers.
+ */
+public final class ControllerCallbackLink implements Parcelable {
+ final CallbackStub mCallbackStub;
+ final ISessionControllerCallback mIControllerCallback;
+
+ /**
+ * Constructor for stub (Callee)
+ * @hide
+ */
+ @SystemApi
+ public ControllerCallbackLink(@NonNull CallbackStub callbackStub) {
+ mCallbackStub = callbackStub;
+ mIControllerCallback = new CallbackStubProxy();
+ }
+
+ /**
+ * Constructor for interface (Caller)
+ */
+ ControllerCallbackLink(Parcel in) {
+ mCallbackStub = null;
+ mIControllerCallback = ISessionControllerCallback.Stub.asInterface(in.readStrongBinder());
+ }
+
+ /**
+ * Notify controller that the connected session is destroyed.
+ */
+ public void notifySessionDestroyed() {
+ try {
+ mIControllerCallback.notifySessionDestroyed();
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Notify controller that the connected session sends an event.
+ *
+ * @param event the name of the event
+ * @param extras the extras included with the event
+ */
+ public void notifyEvent(@NonNull String event, @Nullable Bundle extras) {
+ try {
+ mIControllerCallback.notifyEvent(event, extras);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Notify controller that the current playback state is changed.
+ *
+ * @param state the new playback state
+ */
+ public void notifyPlaybackStateChanged(@Nullable PlaybackState state) {
+ try {
+ mIControllerCallback.notifyPlaybackStateChanged(state);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Notify controller that the current metadata is changed.
+ *
+ * @param metadata the new metadata
+ */
+ public void notifyMetadataChanged(@Nullable MediaMetadata metadata) {
+ try {
+ mIControllerCallback.notifyMetadataChanged(metadata);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Notify controller that the current queue is changed.
+ *
+ * @param queue the new queue
+ */
+ public void notifyQueueChanged(@Nullable List<QueueItem> queue) {
+ try {
+ mIControllerCallback.notifyQueueChanged(queue);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Notify controller that the current queue title is changed.
+ *
+ * @param title the new queue title
+ */
+ public void notifyQueueTitleChanged(@Nullable CharSequence title) {
+ try {
+ mIControllerCallback.notifyQueueTitleChanged(title);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Notify controller that the extras are changed.
+ *
+ * @param extras the new extras
+ */
+ public void notifyExtrasChanged(@Nullable Bundle extras) {
+ try {
+ mIControllerCallback.notifyExtrasChanged(extras);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Notify controller that the playback info is changed.
+ *
+ * @param info the new playback info
+ */
+ public void notifyVolumeInfoChanged(@NonNull PlaybackInfo info) {
+ try {
+ mIControllerCallback.notifyVolumeInfoChanged(info);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /** Gets the binder */
+ @NonNull
+ public IBinder getBinder() {
+ return mIControllerCallback.asBinder();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeStrongBinder(mIControllerCallback.asBinder());
+ }
+
+ public static final Parcelable.Creator<ControllerCallbackLink> CREATOR =
+ new Parcelable.Creator<ControllerCallbackLink>() {
+ @Override
+ public ControllerCallbackLink createFromParcel(Parcel in) {
+ return new ControllerCallbackLink(in);
+ }
+
+ @Override
+ public ControllerCallbackLink[] newArray(int size) {
+ return new ControllerCallbackLink[size];
+ }
+ };
+
+ /**
+ * Class for Stub implementation
+ * @hide
+ */
+ @SystemApi
+ public abstract static class CallbackStub {
+ /** Stub method for ISessionControllerCallback.notifySessionDestroyed */
+ public void onSessionDestroyed() {
+ }
+
+ /** Stub method for ISessionControllerCallback.notifyEvent */
+ public void onEvent(@NonNull String event, @Nullable Bundle extras) {
+ }
+
+ /** Stub method for ISessionControllerCallback.notifyPlaybackStateChanged */
+ public void onPlaybackStateChanged(@Nullable PlaybackState state) {
+ }
+
+ /** Stub method for ISessionControllerCallback.notifyMetadataChanged */
+ public void onMetadataChanged(@Nullable MediaMetadata metadata) {
+ }
+
+ /** Stub method for ISessionControllerCallback.notifyQueueChanged */
+ public void onQueueChanged(@Nullable List<QueueItem> queue) {
+ }
+
+ /** Stub method for ISessionControllerCallback.notifyQueueTitleChanged */
+ public void onQueueTitleChanged(@Nullable CharSequence title) {
+ }
+
+ /** Stub method for ISessionControllerCallback.notifyExtrasChanged */
+ public void onExtrasChanged(@Nullable Bundle extras) {
+ }
+
+ /** Stub method for ISessionControllerCallback.notifyVolumeInfoChanged */
+ public void onVolumeInfoChanged(@NonNull PlaybackInfo info) {
+ }
+ }
+
+ private class CallbackStubProxy extends ISessionControllerCallback.Stub {
+ @Override
+ public void notifyEvent(String event, Bundle extras) {
+ mCallbackStub.onEvent(event, extras);
+ }
+
+ @Override
+ public void notifySessionDestroyed() {
+ mCallbackStub.onSessionDestroyed();
+ }
+
+ @Override
+ public void notifyPlaybackStateChanged(PlaybackState state) {
+ mCallbackStub.onPlaybackStateChanged(state);
+ }
+
+ @Override
+ public void notifyMetadataChanged(MediaMetadata metadata) {
+ mCallbackStub.onMetadataChanged(metadata);
+ }
+
+ @Override
+ public void notifyQueueChanged(List<QueueItem> queue) {
+ mCallbackStub.onQueueChanged(queue);
+ }
+
+ @Override
+ public void notifyQueueTitleChanged(CharSequence title) {
+ mCallbackStub.onQueueTitleChanged(title);
+ }
+
+ @Override
+ public void notifyExtrasChanged(Bundle extras) {
+ mCallbackStub.onExtrasChanged(extras);
+ }
+
+ @Override
+ public void notifyVolumeInfoChanged(PlaybackInfo info) {
+ mCallbackStub.onVolumeInfoChanged(info);
+ }
+ }
+}
diff --git a/media/java/android/media/session/ISession.aidl b/media/java/android/media/session/ISession.aidl
index bfc05fa..1524ad9 100644
--- a/media/java/android/media/session/ISession.aidl
+++ b/media/java/android/media/session/ISession.aidl
@@ -16,7 +16,6 @@
package android.media.session;
import android.app.PendingIntent;
-import android.content.pm.ParceledListSlice;
import android.media.AudioAttributes;
import android.media.MediaMetadata;
import android.media.session.ISessionController;
@@ -41,7 +40,8 @@
// These commands are for the TransportPerformer
void setMetadata(in MediaMetadata metadata, long duration, String metadataDescription);
void setPlaybackState(in PlaybackState state);
- void setQueue(in ParceledListSlice queue);
+ // TODO(b/122432476): Replace List with MediaParceledListSlice
+ void setQueue(in List<MediaSession.QueueItem> queue);
void setQueueTitle(CharSequence title);
void setExtras(in Bundle extras);
void setRatingType(int type);
diff --git a/media/java/android/media/session/ISessionCallback.aidl b/media/java/android/media/session/ISessionCallback.aidl
index 626338d..9b86bfc 100644
--- a/media/java/android/media/session/ISessionCallback.aidl
+++ b/media/java/android/media/session/ISessionCallback.aidl
@@ -17,7 +17,7 @@
import android.content.Intent;
import android.media.Rating;
-import android.media.session.ISessionControllerCallback;
+import android.media.session.ControllerCallbackLink;
import android.net.Uri;
import android.os.Bundle;
import android.os.ResultReceiver;
@@ -26,46 +26,46 @@
* @hide
*/
oneway interface ISessionCallback {
- void onCommand(String packageName, int pid, int uid, ISessionControllerCallback caller,
+ void notifyCommand(String packageName, int pid, int uid, in ControllerCallbackLink caller,
String command, in Bundle args, in ResultReceiver cb);
- void onMediaButton(String packageName, int pid, int uid, in Intent mediaButtonIntent,
+ void notifyMediaButton(String packageName, int pid, int uid, in Intent mediaButtonIntent,
int sequenceNumber, in ResultReceiver cb);
- void onMediaButtonFromController(String packageName, int pid, int uid,
- ISessionControllerCallback caller, in Intent mediaButtonIntent);
+ void notifyMediaButtonFromController(String packageName, int pid, int uid,
+ in ControllerCallbackLink caller, in Intent mediaButtonIntent);
// These callbacks are for the TransportPerformer
- void onPrepare(String packageName, int pid, int uid, ISessionControllerCallback caller);
- void onPrepareFromMediaId(String packageName, int pid, int uid,
- ISessionControllerCallback caller, String mediaId, in Bundle extras);
- void onPrepareFromSearch(String packageName, int pid, int uid,
- ISessionControllerCallback caller, String query, in Bundle extras);
- void onPrepareFromUri(String packageName, int pid, int uid, ISessionControllerCallback caller,
+ void notifyPrepare(String packageName, int pid, int uid, in ControllerCallbackLink caller);
+ void notifyPrepareFromMediaId(String packageName, int pid, int uid,
+ in ControllerCallbackLink caller, String mediaId, in Bundle extras);
+ void notifyPrepareFromSearch(String packageName, int pid, int uid,
+ in ControllerCallbackLink caller, String query, in Bundle extras);
+ void notifyPrepareFromUri(String packageName, int pid, int uid,
+ in ControllerCallbackLink caller, in Uri uri, in Bundle extras);
+ void notifyPlay(String packageName, int pid, int uid, in ControllerCallbackLink caller);
+ void notifyPlayFromMediaId(String packageName, int pid, int uid,
+ in ControllerCallbackLink caller, String mediaId, in Bundle extras);
+ void notifyPlayFromSearch(String packageName, int pid, int uid,
+ in ControllerCallbackLink caller, String query, in Bundle extras);
+ void notifyPlayFromUri(String packageName, int pid, int uid, in ControllerCallbackLink caller,
in Uri uri, in Bundle extras);
- void onPlay(String packageName, int pid, int uid, ISessionControllerCallback caller);
- void onPlayFromMediaId(String packageName, int pid, int uid, ISessionControllerCallback caller,
- String mediaId, in Bundle extras);
- void onPlayFromSearch(String packageName, int pid, int uid, ISessionControllerCallback caller,
- String query, in Bundle extras);
- void onPlayFromUri(String packageName, int pid, int uid, ISessionControllerCallback caller,
- in Uri uri, in Bundle extras);
- void onSkipToTrack(String packageName, int pid, int uid, ISessionControllerCallback caller,
+ void notifySkipToTrack(String packageName, int pid, int uid, in ControllerCallbackLink caller,
long id);
- void onPause(String packageName, int pid, int uid, ISessionControllerCallback caller);
- void onStop(String packageName, int pid, int uid, ISessionControllerCallback caller);
- void onNext(String packageName, int pid, int uid, ISessionControllerCallback caller);
- void onPrevious(String packageName, int pid, int uid, ISessionControllerCallback caller);
- void onFastForward(String packageName, int pid, int uid, ISessionControllerCallback caller);
- void onRewind(String packageName, int pid, int uid, ISessionControllerCallback caller);
- void onSeekTo(String packageName, int pid, int uid, ISessionControllerCallback caller,
+ void notifyPause(String packageName, int pid, int uid, in ControllerCallbackLink caller);
+ void notifyStop(String packageName, int pid, int uid, in ControllerCallbackLink caller);
+ void notifyNext(String packageName, int pid, int uid, in ControllerCallbackLink caller);
+ void notifyPrevious(String packageName, int pid, int uid, in ControllerCallbackLink caller);
+ void notifyFastForward(String packageName, int pid, int uid, in ControllerCallbackLink caller);
+ void notifyRewind(String packageName, int pid, int uid, in ControllerCallbackLink caller);
+ void notifySeekTo(String packageName, int pid, int uid, in ControllerCallbackLink caller,
long pos);
- void onRate(String packageName, int pid, int uid, ISessionControllerCallback caller,
+ void notifyRate(String packageName, int pid, int uid, in ControllerCallbackLink caller,
in Rating rating);
- void onCustomAction(String packageName, int pid, int uid, ISessionControllerCallback caller,
+ void notifyCustomAction(String packageName, int pid, int uid, in ControllerCallbackLink caller,
String action, in Bundle args);
// These callbacks are for volume handling
- void onAdjustVolume(String packageName, int pid, int uid, ISessionControllerCallback caller,
+ void notifyAdjustVolume(String packageName, int pid, int uid, in ControllerCallbackLink caller,
int direction);
- void onSetVolumeTo(String packageName, int pid, int uid,
- ISessionControllerCallback caller, int value);
+ void notifySetVolumeTo(String packageName, int pid, int uid,
+ in ControllerCallbackLink caller, int value);
}
diff --git a/media/java/android/media/session/ISessionController.aidl b/media/java/android/media/session/ISessionController.aidl
index a843881..2ba09fd 100644
--- a/media/java/android/media/session/ISessionController.aidl
+++ b/media/java/android/media/session/ISessionController.aidl
@@ -17,10 +17,9 @@
import android.app.PendingIntent;
import android.content.Intent;
-import android.content.pm.ParceledListSlice;
import android.media.MediaMetadata;
import android.media.Rating;
-import android.media.session.ISessionControllerCallback;
+import android.media.session.ControllerCallbackLink;
import android.media.session.MediaController;
import android.media.session.MediaSession;
import android.media.session.PlaybackState;
@@ -36,12 +35,12 @@
* @hide
*/
interface ISessionController {
- void sendCommand(String packageName, ISessionControllerCallback caller,
+ void sendCommand(String packageName, in ControllerCallbackLink caller,
String command, in Bundle args, in ResultReceiver cb);
- boolean sendMediaButton(String packageName, ISessionControllerCallback caller,
+ boolean sendMediaButton(String packageName, in ControllerCallbackLink caller,
boolean asSystemService, in KeyEvent mediaButton);
- void registerCallbackListener(String packageName, ISessionControllerCallback cb);
- void unregisterCallbackListener(ISessionControllerCallback cb);
+ void registerCallbackListener(String packageName, in ControllerCallbackLink cb);
+ void unregisterCallbackListener(in ControllerCallbackLink cb);
boolean isTransportControlEnabled();
String getPackageName();
String getTag();
@@ -49,40 +48,40 @@
long getFlags();
MediaController.PlaybackInfo getVolumeAttributes();
void adjustVolume(String packageName, String opPackageName,
- in ISessionControllerCallback caller, boolean asSystemService, int direction,
+ in ControllerCallbackLink caller, boolean asSystemService, int direction,
int flags);
- void setVolumeTo(String packageName, String opPackageName, in ISessionControllerCallback caller,
+ void setVolumeTo(String packageName, String opPackageName, in ControllerCallbackLink caller,
int value, int flags);
// These commands are for the TransportControls
- void prepare(String packageName, ISessionControllerCallback caller);
- void prepareFromMediaId(String packageName, ISessionControllerCallback caller,
+ void prepare(String packageName, in ControllerCallbackLink caller);
+ void prepareFromMediaId(String packageName, in ControllerCallbackLink caller,
String mediaId, in Bundle extras);
- void prepareFromSearch(String packageName, ISessionControllerCallback caller,
+ void prepareFromSearch(String packageName, in ControllerCallbackLink caller,
String string, in Bundle extras);
- void prepareFromUri(String packageName, ISessionControllerCallback caller,
+ void prepareFromUri(String packageName, in ControllerCallbackLink caller,
in Uri uri, in Bundle extras);
- void play(String packageName, ISessionControllerCallback caller);
- void playFromMediaId(String packageName, ISessionControllerCallback caller,
+ void play(String packageName, in ControllerCallbackLink caller);
+ void playFromMediaId(String packageName, in ControllerCallbackLink caller,
String mediaId, in Bundle extras);
- void playFromSearch(String packageName, ISessionControllerCallback caller,
+ void playFromSearch(String packageName, in ControllerCallbackLink caller,
String string, in Bundle extras);
- void playFromUri(String packageName, ISessionControllerCallback caller,
+ void playFromUri(String packageName, in ControllerCallbackLink caller,
in Uri uri, in Bundle extras);
- void skipToQueueItem(String packageName, ISessionControllerCallback caller, long id);
- void pause(String packageName, ISessionControllerCallback caller);
- void stop(String packageName, ISessionControllerCallback caller);
- void next(String packageName, ISessionControllerCallback caller);
- void previous(String packageName, ISessionControllerCallback caller);
- void fastForward(String packageName, ISessionControllerCallback caller);
- void rewind(String packageName, ISessionControllerCallback caller);
- void seekTo(String packageName, ISessionControllerCallback caller, long pos);
- void rate(String packageName, ISessionControllerCallback caller, in Rating rating);
- void sendCustomAction(String packageName, ISessionControllerCallback caller,
+ void skipToQueueItem(String packageName, in ControllerCallbackLink caller, long id);
+ void pause(String packageName, in ControllerCallbackLink caller);
+ void stop(String packageName, in ControllerCallbackLink caller);
+ void next(String packageName, in ControllerCallbackLink caller);
+ void previous(String packageName, in ControllerCallbackLink caller);
+ void fastForward(String packageName, in ControllerCallbackLink caller);
+ void rewind(String packageName, in ControllerCallbackLink caller);
+ void seekTo(String packageName, in ControllerCallbackLink caller, long pos);
+ void rate(String packageName, in ControllerCallbackLink caller, in Rating rating);
+ void sendCustomAction(String packageName, in ControllerCallbackLink caller,
String action, in Bundle args);
MediaMetadata getMetadata();
PlaybackState getPlaybackState();
- ParceledListSlice getQueue();
+ List<MediaSession.QueueItem> getQueue();
CharSequence getQueueTitle();
Bundle getExtras();
int getRatingType();
diff --git a/media/java/android/media/session/ISessionControllerCallback.aidl b/media/java/android/media/session/ISessionControllerCallback.aidl
index fac8897..5c02e7c 100644
--- a/media/java/android/media/session/ISessionControllerCallback.aidl
+++ b/media/java/android/media/session/ISessionControllerCallback.aidl
@@ -15,25 +15,24 @@
package android.media.session;
-import android.content.pm.ParceledListSlice;
import android.media.MediaMetadata;
-import android.media.session.PlaybackState;
import android.media.session.MediaController;
import android.media.session.MediaSession;
+import android.media.session.PlaybackState;
import android.os.Bundle;
/**
* @hide
*/
oneway interface ISessionControllerCallback {
- void onEvent(String event, in Bundle extras);
- void onSessionDestroyed();
+ void notifyEvent(String event, in Bundle extras);
+ void notifySessionDestroyed();
// These callbacks are for the TransportController
- void onPlaybackStateChanged(in PlaybackState state);
- void onMetadataChanged(in MediaMetadata metadata);
- void onQueueChanged(in ParceledListSlice queue);
- void onQueueTitleChanged(CharSequence title);
- void onExtrasChanged(in Bundle extras);
- void onVolumeInfoChanged(in MediaController.PlaybackInfo info);
+ void notifyPlaybackStateChanged(in PlaybackState state);
+ void notifyMetadataChanged(in MediaMetadata metadata);
+ void notifyQueueChanged(in List<MediaSession.QueueItem> queue);
+ void notifyQueueTitleChanged(CharSequence title);
+ void notifyExtrasChanged(in Bundle extras);
+ void notifyVolumeInfoChanged(in MediaController.PlaybackInfo info);
}
diff --git a/media/java/android/media/session/ISessionManager.aidl b/media/java/android/media/session/ISessionManager.aidl
index 51148e2..7ac3ef2 100644
--- a/media/java/android/media/session/ISessionManager.aidl
+++ b/media/java/android/media/session/ISessionManager.aidl
@@ -23,7 +23,7 @@
import android.media.session.IOnMediaKeyListener;
import android.media.session.IOnVolumeKeyLongPressListener;
import android.media.session.ISession;
-import android.media.session.ISessionCallback;
+import android.media.session.SessionCallbackLink;
import android.os.Bundle;
import android.view.KeyEvent;
@@ -32,9 +32,10 @@
* @hide
*/
interface ISessionManager {
- ISession createSession(String packageName, in ISessionCallback cb, String tag, int userId);
+ ISession createSession(String packageName, in SessionCallbackLink cb, String tag, int userId);
void notifySession2Created(in Session2Token sessionToken);
List<IBinder> getSessions(in ComponentName compName, int userId);
+ List<Session2Token> getSession2Tokens(int userId);
void dispatchMediaKeyEvent(String packageName, boolean asSystemService, in KeyEvent keyEvent,
boolean needWakeLock);
void dispatchVolumeKeyEvent(String packageName, String opPackageName, boolean asSystemService,
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index ef2df15..a1b8170c 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -21,12 +21,12 @@
import android.annotation.UnsupportedAppUsage;
import android.app.PendingIntent;
import android.content.Context;
-import android.content.pm.ParceledListSlice;
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.MediaMetadata;
import android.media.Rating;
import android.media.VolumeProvider;
+import android.media.session.MediaSession.QueueItem;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
@@ -72,7 +72,8 @@
private final MediaSession.Token mToken;
private final Context mContext;
- private final CallbackStub mCbStub = new CallbackStub(this);
+ private final ControllerCallbackLink mCbStub =
+ new ControllerCallbackLink(new CallbackStub(this));
private final ArrayList<MessageHandler> mCallbacks = new ArrayList<MessageHandler>();
private final Object mLock = new Object();
@@ -251,10 +252,7 @@
*/
public @Nullable List<MediaSession.QueueItem> getQueue() {
try {
- ParceledListSlice queue = mSessionBinder.getQueue();
- if (queue != null) {
- return queue.getList();
- }
+ return mSessionBinder.getQueue();
} catch (RemoteException e) {
Log.wtf(TAG, "Error calling getQueue.", e);
}
@@ -1113,10 +1111,10 @@
};
}
- private final static class CallbackStub extends ISessionControllerCallback.Stub {
+ private static final class CallbackStub extends ControllerCallbackLink.CallbackStub {
private final WeakReference<MediaController> mController;
- public CallbackStub(MediaController controller) {
+ CallbackStub(MediaController controller) {
mController = new WeakReference<MediaController>(controller);
}
@@ -1153,9 +1151,7 @@
}
@Override
- public void onQueueChanged(ParceledListSlice parceledQueue) {
- List<MediaSession.QueueItem> queue = parceledQueue == null ? null : parceledQueue
- .getList();
+ public void onQueueChanged(List<QueueItem> queue) {
MediaController controller = mController.get();
if (controller != null) {
controller.postMessage(MSG_UPDATE_QUEUE, queue, null);
@@ -1185,7 +1181,6 @@
controller.postMessage(MSG_UPDATE_VOLUME, info, null);
}
}
-
}
private final static class MessageHandler extends Handler {
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index df8cc35..e07cf15 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -24,7 +24,6 @@
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.ParceledListSlice;
import android.media.AudioAttributes;
import android.media.MediaDescription;
import android.media.MediaMetadata;
@@ -130,7 +129,7 @@
private final MediaSession.Token mSessionToken;
private final MediaController mController;
private final ISession mBinder;
- private final CallbackStub mCbStub;
+ private final SessionCallbackLink mCbStub;
// Do not change the name of mCallback. Support lib accesses this by using reflection.
@UnsupportedAppUsage
@@ -173,7 +172,7 @@
}
mMaxBitmapSize = context.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.config_mediaMetadataBitmapMaxSize);
- mCbStub = new CallbackStub(this);
+ mCbStub = new SessionCallbackLink(new CallbackStub(this));
MediaSessionManager manager = (MediaSessionManager) context
.getSystemService(Context.MEDIA_SESSION_SERVICE);
try {
@@ -467,7 +466,7 @@
*/
public void setQueue(@Nullable List<QueueItem> queue) {
try {
- mBinder.setQueue(queue == null ? null : new ParceledListSlice<QueueItem>(queue));
+ mBinder.setQueue(queue);
} catch (RemoteException e) {
Log.wtf("Dead object in setQueue.", e);
}
@@ -1063,7 +1062,7 @@
/**
* @hide
*/
- public static class CallbackStub extends ISessionCallback.Stub {
+ public static final class CallbackStub extends SessionCallbackLink.CallbackStub {
private WeakReference<MediaSession> mMediaSession;
public CallbackStub(MediaSession session) {
@@ -1071,14 +1070,14 @@
}
private static RemoteUserInfo createRemoteUserInfo(String packageName, int pid, int uid,
- ISessionControllerCallback caller) {
+ ControllerCallbackLink caller) {
return new RemoteUserInfo(packageName, pid, uid,
- caller != null ? caller.asBinder() : null);
+ caller != null ? caller.getBinder() : null);
}
@Override
public void onCommand(String packageName, int pid, int uid,
- ISessionControllerCallback caller, String command, Bundle args, ResultReceiver cb) {
+ ControllerCallbackLink caller, String command, Bundle args, ResultReceiver cb) {
MediaSession session = mMediaSession.get();
if (session != null) {
session.dispatchCommand(createRemoteUserInfo(packageName, pid, uid, caller),
@@ -1104,7 +1103,7 @@
@Override
public void onMediaButtonFromController(String packageName, int pid, int uid,
- ISessionControllerCallback caller, Intent mediaButtonIntent) {
+ ControllerCallbackLink caller, Intent mediaButtonIntent) {
MediaSession session = mMediaSession.get();
if (session != null) {
session.dispatchMediaButton(createRemoteUserInfo(packageName, pid, uid, caller),
@@ -1114,7 +1113,7 @@
@Override
public void onPrepare(String packageName, int pid, int uid,
- ISessionControllerCallback caller) {
+ ControllerCallbackLink caller) {
MediaSession session = mMediaSession.get();
if (session != null) {
session.dispatchPrepare(createRemoteUserInfo(packageName, pid, uid, caller));
@@ -1123,7 +1122,7 @@
@Override
public void onPrepareFromMediaId(String packageName, int pid, int uid,
- ISessionControllerCallback caller, String mediaId,
+ ControllerCallbackLink caller, String mediaId,
Bundle extras) {
MediaSession session = mMediaSession.get();
if (session != null) {
@@ -1134,7 +1133,7 @@
@Override
public void onPrepareFromSearch(String packageName, int pid, int uid,
- ISessionControllerCallback caller, String query,
+ ControllerCallbackLink caller, String query,
Bundle extras) {
MediaSession session = mMediaSession.get();
if (session != null) {
@@ -1145,7 +1144,7 @@
@Override
public void onPrepareFromUri(String packageName, int pid, int uid,
- ISessionControllerCallback caller, Uri uri, Bundle extras) {
+ ControllerCallbackLink caller, Uri uri, Bundle extras) {
MediaSession session = mMediaSession.get();
if (session != null) {
session.dispatchPrepareFromUri(createRemoteUserInfo(packageName, pid, uid, caller),
@@ -1155,7 +1154,7 @@
@Override
public void onPlay(String packageName, int pid, int uid,
- ISessionControllerCallback caller) {
+ ControllerCallbackLink caller) {
MediaSession session = mMediaSession.get();
if (session != null) {
session.dispatchPlay(createRemoteUserInfo(packageName, pid, uid, caller));
@@ -1164,7 +1163,7 @@
@Override
public void onPlayFromMediaId(String packageName, int pid, int uid,
- ISessionControllerCallback caller, String mediaId,
+ ControllerCallbackLink caller, String mediaId,
Bundle extras) {
MediaSession session = mMediaSession.get();
if (session != null) {
@@ -1175,7 +1174,7 @@
@Override
public void onPlayFromSearch(String packageName, int pid, int uid,
- ISessionControllerCallback caller, String query,
+ ControllerCallbackLink caller, String query,
Bundle extras) {
MediaSession session = mMediaSession.get();
if (session != null) {
@@ -1186,7 +1185,7 @@
@Override
public void onPlayFromUri(String packageName, int pid, int uid,
- ISessionControllerCallback caller, Uri uri, Bundle extras) {
+ ControllerCallbackLink caller, Uri uri, Bundle extras) {
MediaSession session = mMediaSession.get();
if (session != null) {
session.dispatchPlayFromUri(createRemoteUserInfo(packageName, pid, uid, caller),
@@ -1196,7 +1195,7 @@
@Override
public void onSkipToTrack(String packageName, int pid, int uid,
- ISessionControllerCallback caller, long id) {
+ ControllerCallbackLink caller, long id) {
MediaSession session = mMediaSession.get();
if (session != null) {
session.dispatchSkipToItem(createRemoteUserInfo(packageName, pid, uid, caller), id);
@@ -1205,7 +1204,7 @@
@Override
public void onPause(String packageName, int pid, int uid,
- ISessionControllerCallback caller) {
+ ControllerCallbackLink caller) {
MediaSession session = mMediaSession.get();
if (session != null) {
session.dispatchPause(createRemoteUserInfo(packageName, pid, uid, caller));
@@ -1214,7 +1213,7 @@
@Override
public void onStop(String packageName, int pid, int uid,
- ISessionControllerCallback caller) {
+ ControllerCallbackLink caller) {
MediaSession session = mMediaSession.get();
if (session != null) {
session.dispatchStop(createRemoteUserInfo(packageName, pid, uid, caller));
@@ -1223,7 +1222,7 @@
@Override
public void onNext(String packageName, int pid, int uid,
- ISessionControllerCallback caller) {
+ ControllerCallbackLink caller) {
MediaSession session = mMediaSession.get();
if (session != null) {
session.dispatchNext(createRemoteUserInfo(packageName, pid, uid, caller));
@@ -1232,7 +1231,7 @@
@Override
public void onPrevious(String packageName, int pid, int uid,
- ISessionControllerCallback caller) {
+ ControllerCallbackLink caller) {
MediaSession session = mMediaSession.get();
if (session != null) {
session.dispatchPrevious(createRemoteUserInfo(packageName, pid, uid, caller));
@@ -1241,7 +1240,7 @@
@Override
public void onFastForward(String packageName, int pid, int uid,
- ISessionControllerCallback caller) {
+ ControllerCallbackLink caller) {
MediaSession session = mMediaSession.get();
if (session != null) {
session.dispatchFastForward(createRemoteUserInfo(packageName, pid, uid, caller));
@@ -1250,7 +1249,7 @@
@Override
public void onRewind(String packageName, int pid, int uid,
- ISessionControllerCallback caller) {
+ ControllerCallbackLink caller) {
MediaSession session = mMediaSession.get();
if (session != null) {
session.dispatchRewind(createRemoteUserInfo(packageName, pid, uid, caller));
@@ -1259,7 +1258,7 @@
@Override
public void onSeekTo(String packageName, int pid, int uid,
- ISessionControllerCallback caller, long pos) {
+ ControllerCallbackLink caller, long pos) {
MediaSession session = mMediaSession.get();
if (session != null) {
session.dispatchSeekTo(createRemoteUserInfo(packageName, pid, uid, caller), pos);
@@ -1267,7 +1266,7 @@
}
@Override
- public void onRate(String packageName, int pid, int uid, ISessionControllerCallback caller,
+ public void onRate(String packageName, int pid, int uid, ControllerCallbackLink caller,
Rating rating) {
MediaSession session = mMediaSession.get();
if (session != null) {
@@ -1277,7 +1276,7 @@
@Override
public void onCustomAction(String packageName, int pid, int uid,
- ISessionControllerCallback caller, String action, Bundle args) {
+ ControllerCallbackLink caller, String action, Bundle args) {
MediaSession session = mMediaSession.get();
if (session != null) {
session.dispatchCustomAction(createRemoteUserInfo(packageName, pid, uid, caller),
@@ -1287,7 +1286,7 @@
@Override
public void onAdjustVolume(String packageName, int pid, int uid,
- ISessionControllerCallback caller, int direction) {
+ ControllerCallbackLink caller, int direction) {
MediaSession session = mMediaSession.get();
if (session != null) {
session.dispatchAdjustVolume(createRemoteUserInfo(packageName, pid, uid, caller),
@@ -1297,7 +1296,7 @@
@Override
public void onSetVolumeTo(String packageName, int pid, int uid,
- ISessionControllerCallback caller, int value) {
+ ControllerCallbackLink caller, int value) {
MediaSession session = mMediaSession.get();
if (session != null) {
session.dispatchSetVolumeTo(createRemoteUserInfo(packageName, pid, uid, caller),
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index ef5cf00..56ea484 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -98,7 +98,7 @@
* @return The binder object from the system
* @hide
*/
- public @NonNull ISession createSession(@NonNull MediaSession.CallbackStub cbStub,
+ public @NonNull ISession createSession(@NonNull SessionCallbackLink cbStub,
@NonNull String tag, int userId) throws RemoteException {
return mService.createSession(mContext.getPackageName(), cbStub, tag, userId);
}
@@ -179,6 +179,44 @@
}
/**
+ * Gets a list of {@link Session2Token} with type {@link Session2Token#TYPE_SESSION} for the
+ * current user.
+ * <p>
+ * Although this API can be used without any restriction, each session owners can accept or
+ * reject your uses of {@link MediaSession2}.
+ *
+ * @return A list of {@link Session2Token}.
+ * @hide
+ */
+ // TODO: unhide
+ @NonNull
+ public List<Session2Token> getSession2Tokens() {
+ return getSession2Tokens(UserHandle.myUserId());
+ }
+
+ /**
+ * Gets a list of {@link Session2Token} with type {@link Session2Token#TYPE_SESSION} for the
+ * given user.
+ * <p>
+ * If you want to get tokens for another user, you must hold the
+ * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission.
+ *
+ * @param userId The user id to fetch sessions for.
+ * @return A list of {@link Session2Token}
+ * @hide
+ */
+ // TODO: unhide
+ @NonNull
+ public List<Session2Token> getSession2Tokens(int userId) {
+ try {
+ return mService.getSession2Tokens(userId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to get session tokens", e);
+ }
+ return new ArrayList<>();
+ }
+
+ /**
* Add a listener to be notified when the list of active sessions
* changes.This requires the
* android.Manifest.permission.MEDIA_CONTENT_CONTROL permission be held by
diff --git a/media/java/android/media/session/SessionCallbackLink.aidl b/media/java/android/media/session/SessionCallbackLink.aidl
new file mode 100644
index 0000000..c489e5b
--- /dev/null
+++ b/media/java/android/media/session/SessionCallbackLink.aidl
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media.session;
+
+parcelable SessionCallbackLink;
diff --git a/media/java/android/media/session/SessionCallbackLink.java b/media/java/android/media/session/SessionCallbackLink.java
new file mode 100644
index 0000000..7547bff
--- /dev/null
+++ b/media/java/android/media/session/SessionCallbackLink.java
@@ -0,0 +1,776 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.session;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Intent;
+import android.media.Rating;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+
+/**
+ * Handles incoming commands to {@link MediaSession.Callback}.
+ * <p>
+ * This API is not generally intended for third party application developers.
+ */
+public final class SessionCallbackLink implements Parcelable {
+ final CallbackStub mCallbackStub;
+ final ISessionCallback mISessionCallback;
+
+ /**
+ * Constructor for stub (Callee)
+ */
+ SessionCallbackLink(@NonNull CallbackStub callbackStub) {
+ mCallbackStub = callbackStub;
+ mISessionCallback = new CallbackStubProxy();
+ }
+
+ /**
+ * Constructor for interface (Caller)
+ */
+ SessionCallbackLink(Parcel in) {
+ mCallbackStub = null;
+ mISessionCallback = ISessionCallback.Stub.asInterface(in.readStrongBinder());
+ }
+
+ /**
+ * Notify session that a controller sends a command.
+ *
+ * @param packageName the package name of the controller
+ * @param pid the pid of the controller
+ * @param uid the uid of the controller
+ * @param caller the {@link ControllerCallbackLink} of the controller
+ * @param command the name of the command
+ * @param args the arguments included with the command
+ * @param cb the result receiver for getting the result of the command
+ */
+ public void notifyCommand(@NonNull String packageName, int pid, int uid,
+ @NonNull ControllerCallbackLink caller, @NonNull String command,
+ @Nullable Bundle args, @Nullable ResultReceiver cb) {
+ try {
+ mISessionCallback.notifyCommand(packageName, pid, uid, caller, command, args, cb);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Notify session that the android system sends a media button event.
+ *
+ * @param packageName the package name of the controller
+ * @param pid the pid of the controller
+ * @param uid the uid of the controller
+ * @param mediaButtonIntent the media button intent
+ * @param sequenceNumber the sequence number of this call
+ * @param cb the result receiver for getting the result of the command
+ */
+ public void notifyMediaButton(@NonNull String packageName, int pid, int uid,
+ @NonNull Intent mediaButtonIntent, int sequenceNumber,
+ @Nullable ResultReceiver cb) {
+ try {
+ mISessionCallback.notifyMediaButton(packageName, pid, uid, mediaButtonIntent,
+ sequenceNumber, cb);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Notify session that a controller sends a media button event.
+ *
+ * @param packageName the package name of the controller
+ * @param pid the pid of the controller
+ * @param uid the uid of the controller
+ * @param caller the {@link ControllerCallbackLink} of the controller
+ * @param mediaButtonIntent the media button intent
+ */
+ public void notifyMediaButtonFromController(@NonNull String packageName, int pid, int uid,
+ @NonNull ControllerCallbackLink caller, @NonNull Intent mediaButtonIntent) {
+ try {
+ mISessionCallback.notifyMediaButtonFromController(packageName, pid, uid, caller,
+ mediaButtonIntent);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Notify session that a controller requests preparing media.
+ *
+ * @param packageName the package name of the controller
+ * @param pid the pid of the controller
+ * @param uid the uid of the controller
+ * @param caller the {@link ControllerCallbackLink} of the controller
+ */
+ public void notifyPrepare(@NonNull String packageName, int pid, int uid,
+ @NonNull ControllerCallbackLink caller) {
+ try {
+ mISessionCallback.notifyPrepare(packageName, pid, uid, caller);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Notify session that a controller requests preparing media from given media ID.
+ *
+ * @param packageName the package name of the controller
+ * @param pid the pid of the controller
+ * @param uid the uid of the controller
+ * @param caller the {@link ControllerCallbackLink} of the controller
+ * @param mediaId the ID of the media
+ * @param extras the extras included with this request.
+ */
+ public void notifyPrepareFromMediaId(@NonNull String packageName, int pid, int uid,
+ @NonNull ControllerCallbackLink caller, @NonNull String mediaId,
+ @Nullable Bundle extras) {
+ try {
+ mISessionCallback.notifyPrepareFromMediaId(packageName, pid, uid, caller, mediaId,
+ extras);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Notify session that a controller requests preparing media from given search query.
+ *
+ * @param packageName the package name of the controller
+ * @param pid the pid of the controller
+ * @param uid the uid of the controller
+ * @param caller the {@link ControllerCallbackLink} of the controller
+ * @param query the search query
+ * @param extras the extras included with this request.
+ */
+ public void notifyPrepareFromSearch(@NonNull String packageName, int pid, int uid,
+ @NonNull ControllerCallbackLink caller, @NonNull String query,
+ @Nullable Bundle extras) {
+ try {
+ mISessionCallback.notifyPrepareFromSearch(packageName, pid, uid, caller, query, extras);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Notify session that a controller requests preparing media from given uri.
+ *
+ * @param packageName the package name of the controller
+ * @param pid the pid of the controller
+ * @param uid the uid of the controller
+ * @param caller the {@link ControllerCallbackLink} of the controller
+ * @param uri the uri of the media
+ * @param extras the extras included with this request.
+ */
+ public void notifyPrepareFromUri(@NonNull String packageName, int pid, int uid,
+ @NonNull ControllerCallbackLink caller, @NonNull Uri uri, @Nullable Bundle extras) {
+ try {
+ mISessionCallback.notifyPrepareFromUri(packageName, pid, uid, caller, uri, extras);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Notify session that a controller requests playing media.
+ *
+ * @param packageName the package name of the controller
+ * @param pid the pid of the controller
+ * @param uid the uid of the controller
+ * @param caller the {@link ControllerCallbackLink} of the controller
+ */
+ public void notifyPlay(@NonNull String packageName, int pid, int uid,
+ @NonNull ControllerCallbackLink caller) {
+ try {
+ mISessionCallback.notifyPlay(packageName, pid, uid, caller);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Notify session that a controller requests playing media from given media ID.
+ *
+ * @param packageName the package name of the controller
+ * @param pid the pid of the controller
+ * @param uid the uid of the controller
+ * @param caller the {@link ControllerCallbackLink} of the controller
+ * @param mediaId the ID of the media
+ * @param extras the extras included with this request.
+ */
+ public void notifyPlayFromMediaId(@NonNull String packageName, int pid, int uid,
+ @NonNull ControllerCallbackLink caller, @NonNull String mediaId,
+ @Nullable Bundle extras) {
+ try {
+ mISessionCallback.notifyPlayFromMediaId(packageName, pid, uid, caller, mediaId, extras);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Notify session that a controller requests playing media from given search query.
+ *
+ * @param packageName the package name of the controller
+ * @param pid the pid of the controller
+ * @param uid the uid of the controller
+ * @param caller the {@link ControllerCallbackLink} of the controller
+ * @param query the search query
+ * @param extras the extras included with this request.
+ */
+ public void notifyPlayFromSearch(@NonNull String packageName, int pid, int uid,
+ @NonNull ControllerCallbackLink caller, @NonNull String query,
+ @Nullable Bundle extras) {
+ try {
+ mISessionCallback.notifyPlayFromSearch(packageName, pid, uid, caller, query, extras);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Notify session that a controller requests playing media from given uri.
+ *
+ * @param packageName the package name of the controller
+ * @param pid the pid of the controller
+ * @param uid the uid of the controller
+ * @param caller the {@link ControllerCallbackLink} of the controller
+ * @param uri the uri of the media
+ * @param extras the extras included with this request.
+ */
+ public void notifyPlayFromUri(@NonNull String packageName, int pid, int uid,
+ @NonNull ControllerCallbackLink caller, @NonNull Uri uri, @Nullable Bundle extras) {
+ try {
+ mISessionCallback.notifyPlayFromUri(packageName, pid, uid, caller, uri, extras);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Notify session that a controller requests skipping to the queue item with given ID.
+ *
+ * @param packageName the package name of the controller
+ * @param pid the pid of the controller
+ * @param uid the uid of the controller
+ * @param caller the {@link ControllerCallbackLink} of the controller
+ * @param id the queue id of the item
+ */
+ public void notifySkipToTrack(@NonNull String packageName, int pid, int uid,
+ @NonNull ControllerCallbackLink caller, long id) {
+ try {
+ mISessionCallback.notifySkipToTrack(packageName, pid, uid, caller, id);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Notify session that a controller requests pausing media.
+ *
+ * @param packageName the package name of the controller
+ * @param pid the pid of the controller
+ * @param uid the uid of the controller
+ * @param caller the {@link ControllerCallbackLink} of the controller
+ */
+ public void notifyPause(@NonNull String packageName, int pid, int uid,
+ @NonNull ControllerCallbackLink caller) {
+ try {
+ mISessionCallback.notifyPause(packageName, pid, uid, caller);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Notify session that a controller requests stopping media.
+ *
+ * @param packageName the package name of the controller
+ * @param pid the pid of the controller
+ * @param uid the uid of the controller
+ * @param caller the {@link ControllerCallbackLink} of the controller
+ */
+ public void notifyStop(@NonNull String packageName, int pid, int uid,
+ @NonNull ControllerCallbackLink caller) {
+ try {
+ mISessionCallback.notifyStop(packageName, pid, uid, caller);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Notify session that a controller requests skipping to the next queue item.
+ *
+ * @param packageName the package name of the controller
+ * @param pid the pid of the controller
+ * @param uid the uid of the controller
+ * @param caller the {@link ControllerCallbackLink} of the controller
+ */
+ public void notifyNext(@NonNull String packageName, int pid, int uid,
+ @NonNull ControllerCallbackLink caller) {
+ try {
+ mISessionCallback.notifyNext(packageName, pid, uid, caller);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Notify session that a controller requests skipping to the previous queue item.
+ *
+ * @param packageName the package name of the controller
+ * @param pid the pid of the controller
+ * @param uid the uid of the controller
+ * @param caller the {@link ControllerCallbackLink} of the controller
+ */
+ public void notifyPrevious(@NonNull String packageName, int pid, int uid,
+ @NonNull ControllerCallbackLink caller) {
+ try {
+ mISessionCallback.notifyPrevious(packageName, pid, uid, caller);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Notify session that a controller requests fast-forwarding.
+ *
+ * @param packageName the package name of the controller
+ * @param pid the pid of the controller
+ * @param uid the uid of the controller
+ * @param caller the {@link ControllerCallbackLink} of the controller
+ */
+ public void notifyFastForward(@NonNull String packageName, int pid, int uid,
+ @NonNull ControllerCallbackLink caller) {
+ try {
+ mISessionCallback.notifyFastForward(packageName, pid, uid, caller);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Notify session that a controller requests rewinding.
+ *
+ * @param packageName the package name of the controller
+ * @param pid the pid of the controller
+ * @param uid the uid of the controller
+ * @param caller the {@link ControllerCallbackLink} of the controller
+ */
+ public void notifyRewind(@NonNull String packageName, int pid, int uid,
+ @NonNull ControllerCallbackLink caller) {
+ try {
+ mISessionCallback.notifyRewind(packageName, pid, uid, caller);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Notify session that a controller requests seeking to the specific position.
+ *
+ * @param packageName the package name of the controller
+ * @param pid the pid of the controller
+ * @param uid the uid of the controller
+ * @param caller the {@link ControllerCallbackLink} of the controller
+ * @param pos the position to move to, in milliseconds
+ */
+ public void notifySeekTo(@NonNull String packageName, int pid, int uid,
+ @NonNull ControllerCallbackLink caller, long pos) {
+ try {
+ mISessionCallback.notifySeekTo(packageName, pid, uid, caller, pos);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Notify session that a controller requests rating of the current media.
+ *
+ * @param packageName the package name of the controller
+ * @param pid the pid of the controller
+ * @param uid the uid of the controller
+ * @param caller the {@link ControllerCallbackLink} of the controller
+ * @param rating the rating of the current media
+ */
+ public void notifyRate(@NonNull String packageName, int pid, int uid,
+ @NonNull ControllerCallbackLink caller, @NonNull Rating rating) {
+ try {
+ mISessionCallback.notifyRate(packageName, pid, uid, caller, rating);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Notify session that a controller sends a custom action.
+ *
+ * @param packageName the package name of the controller
+ * @param pid the pid of the controller
+ * @param uid the uid of the controller
+ * @param caller the {@link ControllerCallbackLink} of the controller
+ * @param action the name of the action
+ * @param args the arguments included with this action
+ */
+ public void notifyCustomAction(@NonNull String packageName, int pid, int uid,
+ @NonNull ControllerCallbackLink caller, @NonNull String action, @Nullable Bundle args) {
+ try {
+ mISessionCallback.notifyCustomAction(packageName, pid, uid, caller, action, args);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Notify session that a controller requests adjusting volume.
+ *
+ * @param packageName the package name of the controller
+ * @param pid the pid of the controller
+ * @param uid the uid of the controller
+ * @param caller the {@link ControllerCallbackLink} of the controller
+ * @param direction the direction of the volume change.
+ */
+ public void notifyAdjustVolume(@NonNull String packageName, int pid, int uid,
+ @NonNull ControllerCallbackLink caller, int direction) {
+ try {
+ mISessionCallback.notifyAdjustVolume(packageName, pid, uid, caller, direction);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Notify session that a controller requests setting volume.
+ *
+ * @param packageName the package name of the controller
+ * @param pid the pid of the controller
+ * @param uid the uid of the controller
+ * @param caller the {@link ControllerCallbackLink} of the controller
+ * @param value the volume value to set
+ */
+ public void notifySetVolumeTo(@NonNull String packageName, int pid, int uid,
+ @NonNull ControllerCallbackLink caller, int value) {
+ try {
+ mISessionCallback.notifySetVolumeTo(packageName, pid, uid, caller, value);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /** Gets the binder */
+ @NonNull
+ public IBinder getBinder() {
+ return mISessionCallback.asBinder();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeStrongBinder(mISessionCallback.asBinder());
+ }
+
+ public static final Parcelable.Creator<SessionCallbackLink> CREATOR =
+ new Parcelable.Creator<SessionCallbackLink>() {
+ @Override
+ public SessionCallbackLink createFromParcel(Parcel in) {
+ return new SessionCallbackLink(in);
+ }
+
+ @Override
+ public SessionCallbackLink[] newArray(int size) {
+ return new SessionCallbackLink[size];
+ }
+ };
+
+ /**
+ * Class for Stub implementation
+ */
+ abstract static class CallbackStub {
+ /** Stub method for ISessionCallback.notifyCommand */
+ public void onCommand(@NonNull String packageName, int pid, int uid,
+ @NonNull ControllerCallbackLink caller, @NonNull String command,
+ @Nullable Bundle args, @Nullable ResultReceiver cb) {
+ }
+
+ /** Stub method for ISessionCallback.notifyMediaButton */
+ public void onMediaButton(@NonNull String packageName, int pid, int uid,
+ @NonNull Intent mediaButtonIntent, int sequenceNumber,
+ @Nullable ResultReceiver cb) {
+ }
+
+ /** Stub method for ISessionCallback.notifyMediaButtonFromController */
+ public void onMediaButtonFromController(@NonNull String packageName, int pid, int uid,
+ @NonNull ControllerCallbackLink caller, @NonNull Intent mediaButtonIntent) {
+ }
+
+ /** Stub method for ISessionCallback.notifyPrepare */
+ public void onPrepare(@NonNull String packageName, int pid, int uid,
+ @NonNull ControllerCallbackLink caller) {
+ }
+
+ /** Stub method for ISessionCallback.notifyPrepareFromMediaId */
+ public void onPrepareFromMediaId(@NonNull String packageName, int pid, int uid,
+ @NonNull ControllerCallbackLink caller, @NonNull String mediaId,
+ @Nullable Bundle extras) {
+ }
+
+ /** Stub method for ISessionCallback.notifyPrepareFromSearch */
+ public void onPrepareFromSearch(@NonNull String packageName, int pid, int uid,
+ @NonNull ControllerCallbackLink caller, String query, @Nullable Bundle extras) {
+ }
+
+ /** Stub method for ISessionCallback.notifyPrepareFromUri */
+ public void onPrepareFromUri(@NonNull String packageName, int pid, int uid,
+ @NonNull ControllerCallbackLink caller, @NonNull Uri uri, @Nullable Bundle extras) {
+ }
+
+ /** Stub method for ISessionCallback.notifyPlay */
+ public void onPlay(@NonNull String packageName, int pid, int uid,
+ @NonNull ControllerCallbackLink caller) {
+ }
+
+ /** Stub method for ISessionCallback.notifyPlayFromMediaId */
+ public void onPlayFromMediaId(@NonNull String packageName, int pid, int uid,
+ @NonNull ControllerCallbackLink caller, @NonNull String mediaId,
+ @Nullable Bundle extras) {
+ }
+
+ /** Stub method for ISessionCallback.notifyPlayFromSearch */
+ public void onPlayFromSearch(@NonNull String packageName, int pid, int uid,
+ @NonNull ControllerCallbackLink caller, String query, @Nullable Bundle extras) {
+ }
+
+ /** Stub method for ISessionCallback.notifyPlayFromUri */
+ public void onPlayFromUri(@NonNull String packageName, int pid, int uid,
+ @NonNull ControllerCallbackLink caller, @NonNull Uri uri, @Nullable Bundle extras) {
+ }
+
+ /** Stub method for ISessionCallback.notifySkipToTrack */
+ public void onSkipToTrack(@NonNull String packageName, int pid, int uid,
+ @NonNull ControllerCallbackLink caller, long id) {
+ }
+
+ /** Stub method for ISessionCallback.notifyPause */
+ public void onPause(@NonNull String packageName, int pid, int uid,
+ @NonNull ControllerCallbackLink caller) {
+ }
+
+ /** Stub method for ISessionCallback.notifyStop */
+ public void onStop(@NonNull String packageName, int pid, int uid,
+ @NonNull ControllerCallbackLink caller) {
+ }
+
+ /** Stub method for ISessionCallback.notifyNext */
+ public void onNext(@NonNull String packageName, int pid, int uid,
+ @NonNull ControllerCallbackLink caller) {
+ }
+
+ /** Stub method for ISessionCallback.notifyPrevious */
+ public void onPrevious(@NonNull String packageName, int pid, int uid,
+ @NonNull ControllerCallbackLink caller) {
+ }
+
+ /** Stub method for ISessionCallback.notifyFastForward */
+ public void onFastForward(@NonNull String packageName, int pid, int uid,
+ @NonNull ControllerCallbackLink caller) {
+ }
+
+ /** Stub method for ISessionCallback.notifyRewind */
+ public void onRewind(@NonNull String packageName, int pid, int uid,
+ @NonNull ControllerCallbackLink caller) {
+ }
+
+ /** Stub method for ISessionCallback.notifySeekTo */
+ public void onSeekTo(@NonNull String packageName, int pid, int uid,
+ @NonNull ControllerCallbackLink caller, long pos) {
+ }
+
+ /** Stub method for ISessionCallback.notifyRate */
+ public void onRate(@NonNull String packageName, int pid, int uid,
+ @NonNull ControllerCallbackLink caller, @NonNull Rating rating) {
+ }
+
+ /** Stub method for ISessionCallback.notifyCustomAction */
+ public void onCustomAction(@NonNull String packageName, int pid, int uid,
+ @NonNull ControllerCallbackLink caller, @NonNull String action,
+ @Nullable Bundle args) {
+ }
+
+ /** Stub method for ISessionCallback.notifyAdjustVolume */
+ public void onAdjustVolume(@NonNull String packageName, int pid, int uid,
+ @NonNull ControllerCallbackLink caller, int direction) {
+ }
+
+ /** Stub method for ISessionCallback.notifySetVolumeTo */
+ public void onSetVolumeTo(@NonNull String packageName, int pid, int uid,
+ @NonNull ControllerCallbackLink caller, int value) {
+ }
+ }
+
+ private class CallbackStubProxy extends ISessionCallback.Stub {
+ @Override
+ public void notifyCommand(String packageName, int pid, int uid,
+ ControllerCallbackLink caller, String command, Bundle args, ResultReceiver cb) {
+ mCallbackStub.onCommand(packageName, pid, uid, caller, command, args, cb);
+ }
+
+ @Override
+ public void notifyMediaButton(String packageName, int pid, int uid,
+ Intent mediaButtonIntent, int sequenceNumber, ResultReceiver cb) {
+ mCallbackStub.onMediaButton(packageName, pid, uid, mediaButtonIntent, sequenceNumber,
+ cb);
+ }
+
+ @Override
+ public void notifyMediaButtonFromController(String packageName, int pid, int uid,
+ ControllerCallbackLink caller, Intent mediaButtonIntent) {
+ mCallbackStub.onMediaButtonFromController(packageName, pid, uid, caller,
+ mediaButtonIntent);
+ }
+
+ @Override
+ public void notifyPrepare(String packageName, int pid, int uid,
+ ControllerCallbackLink caller) {
+ mCallbackStub.onPrepare(packageName, pid, uid, caller);
+ }
+
+ @Override
+ public void notifyPrepareFromMediaId(String packageName, int pid, int uid,
+ ControllerCallbackLink caller, String mediaId, Bundle extras) {
+ mCallbackStub.onPrepareFromMediaId(packageName, pid, uid, caller, mediaId, extras);
+ }
+
+ @Override
+ public void notifyPrepareFromSearch(String packageName, int pid, int uid,
+ ControllerCallbackLink caller, String query, Bundle extras) {
+ mCallbackStub.onPrepareFromSearch(packageName, pid, uid, caller, query, extras);
+ }
+
+ @Override
+ public void notifyPrepareFromUri(String packageName, int pid, int uid,
+ ControllerCallbackLink caller, Uri uri, Bundle extras) {
+ mCallbackStub.onPrepareFromUri(packageName, pid, uid, caller, uri, extras);
+ }
+
+ @Override
+ public void notifyPlay(String packageName, int pid, int uid,
+ ControllerCallbackLink caller) {
+ mCallbackStub.onPlay(packageName, pid, uid, caller);
+ }
+
+ @Override
+ public void notifyPlayFromMediaId(String packageName, int pid, int uid,
+ ControllerCallbackLink caller, String mediaId, Bundle extras) {
+ mCallbackStub.onPlayFromMediaId(packageName, pid, uid, caller, mediaId, extras);
+ }
+
+ @Override
+ public void notifyPlayFromSearch(String packageName, int pid, int uid,
+ ControllerCallbackLink caller, String query, Bundle extras) {
+ mCallbackStub.onPlayFromSearch(packageName, pid, uid, caller, query, extras);
+ }
+
+ @Override
+ public void notifyPlayFromUri(String packageName, int pid, int uid,
+ ControllerCallbackLink caller, Uri uri, Bundle extras) {
+ mCallbackStub.onPlayFromUri(packageName, pid, uid, caller, uri, extras);
+ }
+
+ @Override
+ public void notifySkipToTrack(String packageName, int pid, int uid,
+ ControllerCallbackLink caller, long id) {
+ mCallbackStub.onSkipToTrack(packageName, pid, uid, caller, id);
+ }
+
+ @Override
+ public void notifyPause(String packageName, int pid, int uid,
+ ControllerCallbackLink caller) {
+ mCallbackStub.onPause(packageName, pid, uid, caller);
+ }
+
+ @Override
+ public void notifyStop(String packageName, int pid, int uid,
+ ControllerCallbackLink caller) {
+ mCallbackStub.onStop(packageName, pid, uid, caller);
+ }
+
+ @Override
+ public void notifyNext(String packageName, int pid, int uid,
+ ControllerCallbackLink caller) {
+ mCallbackStub.onNext(packageName, pid, uid, caller);
+ }
+
+ @Override
+ public void notifyPrevious(String packageName, int pid, int uid,
+ ControllerCallbackLink caller) {
+ mCallbackStub.onPrevious(packageName, pid, uid, caller);
+ }
+
+ @Override
+ public void notifyFastForward(String packageName, int pid, int uid,
+ ControllerCallbackLink caller) {
+ mCallbackStub.onFastForward(packageName, pid, uid, caller);
+ }
+
+ @Override
+ public void notifyRewind(String packageName, int pid, int uid,
+ ControllerCallbackLink caller) {
+ mCallbackStub.onRewind(packageName, pid, uid, caller);
+ }
+
+ @Override
+ public void notifySeekTo(String packageName, int pid, int uid,
+ ControllerCallbackLink caller, long pos) {
+ mCallbackStub.onSeekTo(packageName, pid, uid, caller, pos);
+ }
+
+ @Override
+ public void notifyRate(String packageName, int pid, int uid, ControllerCallbackLink caller,
+ Rating rating) {
+ mCallbackStub.onRate(packageName, pid, uid, caller, rating);
+ }
+
+ @Override
+ public void notifyCustomAction(String packageName, int pid, int uid,
+ ControllerCallbackLink caller, String action, Bundle args) {
+ mCallbackStub.onCustomAction(packageName, pid, uid, caller, action, args);
+ }
+
+ @Override
+ public void notifyAdjustVolume(String packageName, int pid, int uid,
+ ControllerCallbackLink caller, int direction) {
+ mCallbackStub.onAdjustVolume(packageName, pid, uid, caller, direction);
+ }
+
+ @Override
+ public void notifySetVolumeTo(String packageName, int pid, int uid,
+ ControllerCallbackLink caller, int value) {
+ mCallbackStub.onSetVolumeTo(packageName, pid, uid, caller, value);
+ }
+ }
+}
diff --git a/media/lib/signer/Android.bp b/media/lib/signer/Android.bp
index 8c43683..44f8725 100644
--- a/media/lib/signer/Android.bp
+++ b/media/lib/signer/Android.bp
@@ -18,5 +18,7 @@
name: "com.android.mediadrm.signer",
srcs: ["java/**/*.java"],
api_packages: ["com.android.mediadrm.signer"],
- metalava_enabled: false,
+ srcs_lib: "framework",
+ srcs_lib_whitelist_dirs: ["media/java"],
+ srcs_lib_whitelist_pkgs: ["android.media"],
}
diff --git a/packages/NetworkStack/Android.bp b/packages/NetworkStack/Android.bp
index 4688848..2f7d599 100644
--- a/packages/NetworkStack/Android.bp
+++ b/packages/NetworkStack/Android.bp
@@ -21,10 +21,10 @@
installable: true,
srcs: [
"src/**/*.java",
+ ":services-networkstack-shared-srcs",
],
static_libs: [
"dhcp-packet-lib",
- "frameworks-net-shared-utils",
]
}
diff --git a/packages/NetworkStack/AndroidManifest.xml b/packages/NetworkStack/AndroidManifest.xml
index 8516d94..0b0f1ec 100644
--- a/packages/NetworkStack/AndroidManifest.xml
+++ b/packages/NetworkStack/AndroidManifest.xml
@@ -22,8 +22,11 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+ <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
+ <uses-permission android:name="android.permission.NETWORK_SETTINGS" />
<!-- Launch captive portal app as specific user -->
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
+ <uses-permission android:name="android.permission.NETWORK_STACK" />
<application
android:label="NetworkStack"
android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/NetworkStack/src/android/net/util/SharedLog.java b/packages/NetworkStack/src/android/net/util/SharedLog.java
index 74bc147..4fabf10 100644
--- a/packages/NetworkStack/src/android/net/util/SharedLog.java
+++ b/packages/NetworkStack/src/android/net/util/SharedLog.java
@@ -69,6 +69,10 @@
mComponent = component;
}
+ public String getTag() {
+ return mTag;
+ }
+
/**
* Create a SharedLog based on this log with an additional component prefix on each logged line.
*/
diff --git a/services/net/java/android/net/util/Stopwatch.java b/packages/NetworkStack/src/android/net/util/Stopwatch.java
similarity index 78%
rename from services/net/java/android/net/util/Stopwatch.java
rename to packages/NetworkStack/src/android/net/util/Stopwatch.java
index cb15ee5..c316699 100644
--- a/services/net/java/android/net/util/Stopwatch.java
+++ b/packages/NetworkStack/src/android/net/util/Stopwatch.java
@@ -38,9 +38,9 @@
return (isStarted() && !isStopped());
}
- // Returning |this| makes possible the following usage pattern:
- //
- // Stopwatch s = new Stopwatch().start();
+ /**
+ * Start the Stopwatch.
+ */
public Stopwatch start() {
if (!isStarted()) {
mStartTimeMs = SystemClock.elapsedRealtime();
@@ -48,7 +48,10 @@
return this;
}
- // Returns the total time recorded, in milliseconds, or 0 if not started.
+ /**
+ * Stop the Stopwatch.
+ * @return the total time recorded, in milliseconds, or 0 if not started.
+ */
public long stop() {
if (isRunning()) {
mStopTimeMs = SystemClock.elapsedRealtime();
@@ -57,9 +60,11 @@
return (mStopTimeMs - mStartTimeMs);
}
- // Returns the total time recorded to date, in milliseconds.
- // If the Stopwatch is not running, returns the same value as stop(),
- // i.e. either the total time recorded before stopping or 0.
+ /**
+ * Return the total time recorded to date, in milliseconds.
+ * If the Stopwatch is not running, returns the same value as stop(),
+ * i.e. either the total time recorded before stopping or 0.
+ */
public long lap() {
if (isRunning()) {
return (SystemClock.elapsedRealtime() - mStartTimeMs);
@@ -68,6 +73,9 @@
}
}
+ /**
+ * Reset the Stopwatch. It will be stopped when this method returns.
+ */
public void reset() {
mStartTimeMs = 0;
mStopTimeMs = 0;
diff --git a/packages/NetworkStack/src/com/android/server/NetworkStackService.java b/packages/NetworkStack/src/com/android/server/NetworkStackService.java
index 7fea1e0..057012d 100644
--- a/packages/NetworkStack/src/com/android/server/NetworkStackService.java
+++ b/packages/NetworkStack/src/com/android/server/NetworkStackService.java
@@ -25,18 +25,31 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Service;
+import android.content.Context;
import android.content.Intent;
+import android.net.ConnectivityManager;
+import android.net.INetworkMonitor;
+import android.net.INetworkMonitorCallbacks;
import android.net.INetworkStackConnector;
+import android.net.Network;
+import android.net.NetworkRequest;
+import android.net.PrivateDnsConfigParcel;
import android.net.dhcp.DhcpServer;
import android.net.dhcp.DhcpServingParams;
import android.net.dhcp.DhcpServingParamsParcel;
import android.net.dhcp.IDhcpServerCallbacks;
+import android.net.shared.PrivateDnsConfig;
import android.net.util.SharedLog;
import android.os.IBinder;
import android.os.RemoteException;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.connectivity.NetworkMonitor;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.ArrayDeque;
/**
* Android service used to start the network stack when bound to via an intent.
@@ -52,17 +65,41 @@
* <p>On platforms where the network stack runs in the system server process, this method may
* be called directly instead of obtaining the connector by binding to the service.
*/
- public static IBinder makeConnector() {
- return new NetworkStackConnector();
+ public static IBinder makeConnector(Context context) {
+ return new NetworkStackConnector(context);
}
@NonNull
@Override
public IBinder onBind(Intent intent) {
- return makeConnector();
+ return makeConnector(this);
}
private static class NetworkStackConnector extends INetworkStackConnector.Stub {
+ private static final int NUM_VALIDATION_LOG_LINES = 20;
+ private final Context mContext;
+ private final ConnectivityManager mCm;
+
+ private static final int MAX_VALIDATION_LOGS = 10;
+ @GuardedBy("mValidationLogs")
+ private final ArrayDeque<SharedLog> mValidationLogs = new ArrayDeque<>(MAX_VALIDATION_LOGS);
+
+ private SharedLog addValidationLogs(Network network, String name) {
+ final SharedLog log = new SharedLog(NUM_VALIDATION_LOG_LINES, network + " - " + name);
+ synchronized (mValidationLogs) {
+ while (mValidationLogs.size() >= MAX_VALIDATION_LOGS) {
+ mValidationLogs.removeLast();
+ }
+ mValidationLogs.addFirst(log);
+ }
+ return log;
+ }
+
+ NetworkStackConnector(Context context) {
+ mContext = context;
+ mCm = context.getSystemService(ConnectivityManager.class);
+ }
+
@NonNull
private final SharedLog mLog = new SharedLog(TAG);
@@ -89,11 +126,102 @@
}
@Override
+ public void makeNetworkMonitor(int netId, String name, INetworkMonitorCallbacks cb)
+ throws RemoteException {
+ final Network network = new Network(netId, false /* privateDnsBypass */);
+ final NetworkRequest defaultRequest = mCm.getDefaultRequest();
+ final SharedLog log = addValidationLogs(network, name);
+ final NetworkMonitor nm = new NetworkMonitor(
+ mContext, cb, network, defaultRequest, log);
+ cb.onNetworkMonitorCreated(new NetworkMonitorImpl(nm));
+ }
+
+ @Override
protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter fout,
@Nullable String[] args) {
checkNetworkStackCallingPermission();
- fout.println("NetworkStack logs:");
- mLog.dump(fd, fout, args);
+ final IndentingPrintWriter pw = new IndentingPrintWriter(fout, " ");
+ pw.println("NetworkStack logs:");
+ mLog.dump(fd, pw, args);
+
+ pw.println();
+ pw.println("Validation logs (most recent first):");
+ synchronized (mValidationLogs) {
+ for (SharedLog p : mValidationLogs) {
+ pw.println(p.getTag());
+ pw.increaseIndent();
+ p.dump(fd, pw, args);
+ pw.decreaseIndent();
+ }
+ }
+ }
+ }
+
+ private static class NetworkMonitorImpl extends INetworkMonitor.Stub {
+ private final NetworkMonitor mNm;
+
+ NetworkMonitorImpl(NetworkMonitor nm) {
+ mNm = nm;
+ }
+
+ @Override
+ public void start() {
+ checkNetworkStackCallingPermission();
+ mNm.start();
+ }
+
+ @Override
+ public void launchCaptivePortalApp() {
+ checkNetworkStackCallingPermission();
+ mNm.launchCaptivePortalApp();
+ }
+
+ @Override
+ public void forceReevaluation(int uid) {
+ checkNetworkStackCallingPermission();
+ mNm.forceReevaluation(uid);
+ }
+
+ @Override
+ public void notifyPrivateDnsChanged(PrivateDnsConfigParcel config) {
+ checkNetworkStackCallingPermission();
+ mNm.notifyPrivateDnsSettingsChanged(PrivateDnsConfig.fromParcel(config));
+ }
+
+ @Override
+ public void notifyDnsResponse(int returnCode) {
+ checkNetworkStackCallingPermission();
+ mNm.notifyDnsResponse(returnCode);
+ }
+
+ @Override
+ public void notifySystemReady() {
+ checkNetworkStackCallingPermission();
+ mNm.notifySystemReady();
+ }
+
+ @Override
+ public void notifyNetworkConnected() {
+ checkNetworkStackCallingPermission();
+ mNm.notifyNetworkConnected();
+ }
+
+ @Override
+ public void notifyNetworkDisconnected() {
+ checkNetworkStackCallingPermission();
+ mNm.notifyNetworkDisconnected();
+ }
+
+ @Override
+ public void notifyLinkPropertiesChanged() {
+ checkNetworkStackCallingPermission();
+ mNm.notifyLinkPropertiesChanged();
+ }
+
+ @Override
+ public void notifyNetworkCapabilitiesChanged() {
+ checkNetworkStackCallingPermission();
+ mNm.notifyNetworkCapabilitiesChanged();
}
}
}
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
similarity index 84%
rename from services/core/java/com/android/server/connectivity/NetworkMonitor.java
rename to packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
index 2a00025..94ea1b9 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
@@ -21,6 +21,11 @@
import static android.net.CaptivePortal.APP_RETURN_WANTED_AS_IS;
import static android.net.ConnectivityManager.EXTRA_CAPTIVE_PORTAL_PROBE_SPEC;
import static android.net.ConnectivityManager.EXTRA_CAPTIVE_PORTAL_URL;
+import static android.net.ConnectivityManager.TYPE_MOBILE;
+import static android.net.ConnectivityManager.TYPE_WIFI;
+import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_INVALID;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.metrics.ValidationProbeEvent.DNS_FAILURE;
import static android.net.metrics.ValidationProbeEvent.DNS_SUCCESS;
import static android.net.metrics.ValidationProbeEvent.PROBE_FALLBACK;
@@ -35,6 +40,9 @@
import android.net.CaptivePortal;
import android.net.ConnectivityManager;
import android.net.ICaptivePortal;
+import android.net.INetworkMonitor;
+import android.net.INetworkMonitorCallbacks;
+import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
@@ -46,11 +54,14 @@
import android.net.metrics.IpConnectivityLog;
import android.net.metrics.NetworkEvent;
import android.net.metrics.ValidationProbeEvent;
+import android.net.shared.NetworkMonitorUtils;
+import android.net.shared.PrivateDnsConfig;
+import android.net.util.SharedLog;
import android.net.util.Stopwatch;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
-import android.os.Handler;
import android.os.Message;
+import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
@@ -65,8 +76,6 @@
import android.telephony.CellInfoWcdma;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
-import android.util.LocalLog;
-import android.util.LocalLog.ReadOnlyLocalLog;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
@@ -75,7 +84,6 @@
import com.android.internal.util.RingBufferIndices;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
-import com.android.server.connectivity.DnsManager.PrivateDnsConfig;
import java.io.IOException;
import java.net.HttpURLConnection;
@@ -104,9 +112,7 @@
// Default configuration values for captive portal detection probes.
// TODO: append a random length parameter to the default HTTPS url.
// TODO: randomize browser version ids in the default User-Agent String.
- private static final String DEFAULT_HTTPS_URL = "https://www.google.com/generate_204";
- private static final String DEFAULT_HTTP_URL =
- "http://connectivitycheck.gstatic.com/generate_204";
+ private static final String DEFAULT_HTTPS_URL = "https://www.google.com/generate_204";
private static final String DEFAULT_FALLBACK_URL = "http://www.google.com/gen_204";
private static final String DEFAULT_OTHER_FALLBACK_URLS =
"http://play.googleapis.com/generate_204";
@@ -144,33 +150,12 @@
}
}
- // After a network has been tested this result can be sent with EVENT_NETWORK_TESTED.
- // The network should be used as a default internet connection. It was found to be:
- // 1. a functioning network providing internet access, or
- // 2. a captive portal and the user decided to use it as is.
- public static final int NETWORK_TEST_RESULT_VALID = 0;
- // After a network has been tested this result can be sent with EVENT_NETWORK_TESTED.
- // The network should not be used as a default internet connection. It was found to be:
- // 1. a captive portal and the user is prompted to sign-in, or
- // 2. a captive portal and the user did not want to use it, or
- // 3. a broken network (e.g. DNS failed, connect failed, HTTP request failed).
- public static final int NETWORK_TEST_RESULT_INVALID = 1;
-
private static final int BASE = Protocol.BASE_NETWORK_MONITOR;
-
/**
- * Inform NetworkMonitor that their network is connected.
+ * ConnectivityService has sent a notification to indicate that network has connected.
* Initiates Network Validation.
*/
- public static final int CMD_NETWORK_CONNECTED = BASE + 1;
-
- /**
- * Inform ConnectivityService that the network has been tested.
- * obj = String representing URL that Internet probe was redirect to, if it was redirected.
- * arg1 = One of the NETWORK_TESTED_RESULT_* constants.
- * arg2 = NetID.
- */
- public static final int EVENT_NETWORK_TESTED = BASE + 2;
+ private static final int CMD_NETWORK_CONNECTED = BASE + 1;
/**
* Message to self indicating it's time to evaluate a network's connectivity.
@@ -179,9 +164,9 @@
private static final int CMD_REEVALUATE = BASE + 6;
/**
- * Inform NetworkMonitor that the network has disconnected.
+ * ConnectivityService has sent a notification to indicate that network has disconnected.
*/
- public static final int CMD_NETWORK_DISCONNECTED = BASE + 7;
+ private static final int CMD_NETWORK_DISCONNECTED = BASE + 7;
/**
* Force evaluation even if it has succeeded in the past.
@@ -199,21 +184,13 @@
private static final int CMD_CAPTIVE_PORTAL_APP_FINISHED = BASE + 9;
/**
- * Request ConnectivityService display provisioning notification.
- * arg1 = Whether to make the notification visible.
- * arg2 = NetID.
- * obj = Intent to be launched when notification selected by user, null if !arg1.
- */
- public static final int EVENT_PROVISIONING_NOTIFICATION = BASE + 10;
-
- /**
* Message indicating sign-in app should be launched.
* Sent by mLaunchCaptivePortalAppBroadcastReceiver when the
* user touches the sign in notification, or sent by
* ConnectivityService when the user touches the "sign into
* network" button in the wifi access point detail page.
*/
- public static final int CMD_LAUNCH_CAPTIVE_PORTAL_APP = BASE + 11;
+ private static final int CMD_LAUNCH_CAPTIVE_PORTAL_APP = BASE + 11;
/**
* Retest network to see if captive portal is still in place.
@@ -234,7 +211,6 @@
* validation phase is completed.
*/
private static final int CMD_PRIVATE_DNS_SETTINGS_CHANGED = BASE + 13;
- public static final int EVENT_PRIVATE_DNS_CONFIG_RESOLVED = BASE + 14;
private static final int CMD_EVALUATE_PRIVATE_DNS = BASE + 15;
/**
@@ -263,23 +239,16 @@
// Delay between reevaluations once a captive portal has been found.
private static final int CAPTIVE_PORTAL_REEVALUATE_DELAY_MS = 10 * 60 * 1000;
- private static final int NUM_VALIDATION_LOG_LINES = 20;
-
private String mPrivateDnsProviderHostname = "";
- public static boolean isValidationRequired(
- NetworkCapabilities dfltNetCap, NetworkCapabilities nc) {
- // TODO: Consider requiring validation for DUN networks.
- return dfltNetCap.satisfiedByNetworkCapabilities(nc);
- }
-
private final Context mContext;
- private final Handler mConnectivityServiceHandler;
- private final NetworkAgentInfo mNetworkAgentInfo;
+ private final INetworkMonitorCallbacks mCallback;
private final Network mNetwork;
+ private final Network mNonPrivateDnsBypassNetwork;
private final int mNetId;
private final TelephonyManager mTelephonyManager;
private final WifiManager mWifiManager;
+ private final ConnectivityManager mCm;
private final NetworkRequest mDefaultRequest;
private final IpConnectivityLog mMetricsLog;
private final Dependencies mDependencies;
@@ -292,6 +261,9 @@
@Nullable
private final CaptivePortalProbeSpec[] mCaptivePortalFallbackSpecs;
+ private NetworkCapabilities mNetworkCapabilities;
+ private LinkProperties mLinkProperties;
+
@VisibleForTesting
protected boolean mIsCaptivePortalCheckEnabled;
@@ -304,7 +276,7 @@
// Avoids surfacing "Sign in to network" notification.
private boolean mDontDisplaySigninNotification = false;
- public boolean systemReady = false;
+ private volatile boolean mSystemReady = false;
private final State mDefaultState = new DefaultState();
private final State mValidatedState = new ValidatedState();
@@ -317,7 +289,7 @@
private CustomIntentReceiver mLaunchCaptivePortalAppBroadcastReceiver = null;
- private final LocalLog validationLogs = new LocalLog(NUM_VALIDATION_LOG_LINES);
+ private final SharedLog mValidationLogs;
private final Stopwatch mEvaluationTimer = new Stopwatch();
@@ -328,6 +300,7 @@
private final Random mRandom;
private int mNextFallbackUrlIndex = 0;
+
private int mReevaluateDelayMs = INITIAL_REEVALUATE_DELAY_MS;
private int mEvaluateAttempts = 0;
private volatile int mProbeToken = 0;
@@ -338,17 +311,18 @@
private final DnsStallDetector mDnsStallDetector;
private long mLastProbeTime;
- public NetworkMonitor(Context context, Handler handler, NetworkAgentInfo networkAgentInfo,
- NetworkRequest defaultRequest) {
- this(context, handler, networkAgentInfo, defaultRequest, new IpConnectivityLog(),
+ public NetworkMonitor(Context context, INetworkMonitorCallbacks cb, Network network,
+ NetworkRequest defaultRequest, SharedLog validationLog) {
+ this(context, cb, network, defaultRequest, new IpConnectivityLog(), validationLog,
Dependencies.DEFAULT);
}
@VisibleForTesting
- protected NetworkMonitor(Context context, Handler handler, NetworkAgentInfo networkAgentInfo,
- NetworkRequest defaultRequest, IpConnectivityLog logger, Dependencies deps) {
+ protected NetworkMonitor(Context context, INetworkMonitorCallbacks cb, Network network,
+ NetworkRequest defaultRequest, IpConnectivityLog logger, SharedLog validationLogs,
+ Dependencies deps) {
// Add suffix indicating which NetworkMonitor we're talking about.
- super(TAG + networkAgentInfo.name());
+ super(TAG + "/" + network.netId);
// Logs with a tag of the form given just above, e.g.
// <timestamp> 862 2402 D NetworkMonitor/NetworkAgentInfo [WIFI () - 100]: ...
@@ -356,15 +330,18 @@
mContext = context;
mMetricsLog = logger;
- mConnectivityServiceHandler = handler;
+ mValidationLogs = validationLogs;
+ mCallback = cb;
mDependencies = deps;
- mNetworkAgentInfo = networkAgentInfo;
- mNetwork = deps.getNetwork(networkAgentInfo).getPrivateDnsBypassingCopy();
+ mNonPrivateDnsBypassNetwork = network;
+ mNetwork = deps.getPrivateDnsBypassNetwork(network);
mNetId = mNetwork.netId;
mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
+ mCm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
mDefaultRequest = defaultRequest;
+ // CHECKSTYLE:OFF IndentationCheck
addState(mDefaultState);
addState(mMaybeNotifyState, mDefaultState);
addState(mEvaluatingState, mMaybeNotifyState);
@@ -374,12 +351,13 @@
addState(mEvaluatingPrivateDnsState, mDefaultState);
addState(mValidatedState, mDefaultState);
setInitialState(mDefaultState);
+ // CHECKSTYLE:ON IndentationCheck
mIsCaptivePortalCheckEnabled = getIsCaptivePortalCheckEnabled();
mUseHttps = getUseHttpsValidation();
mCaptivePortalUserAgent = getCaptivePortalUserAgent();
mCaptivePortalHttpsUrl = makeURL(getCaptivePortalServerHttpsUrl());
- mCaptivePortalHttpUrl = makeURL(getCaptivePortalServerHttpUrl(deps, context));
+ mCaptivePortalHttpUrl = makeURL(deps.getCaptivePortalServerHttpUrl(context));
mCaptivePortalFallbackUrls = makeCaptivePortalFallbackUrls();
mCaptivePortalFallbackSpecs = makeCaptivePortalFallbackProbeSpecs();
mRandom = deps.getRandom();
@@ -390,7 +368,13 @@
mDataStallValidDnsTimeThreshold = getDataStallValidDnsTimeThreshold();
mDataStallEvaluationType = getDataStallEvalutionType();
- start();
+ // mLinkProperties and mNetworkCapbilities must never be null or we will NPE.
+ // Provide empty objects in case we are started and the network disconnects before
+ // we can ever fetch them.
+ // TODO: Delete ASAP.
+ mLinkProperties = new LinkProperties();
+ mNetworkCapabilities = new NetworkCapabilities();
+ mNetworkCapabilities.clearAll();
}
/**
@@ -401,6 +385,14 @@
}
/**
+ * Send a notification to NetworkMonitor indicating that there was a DNS query response event.
+ * @param returnCode the DNS return code of the response.
+ */
+ public void notifyDnsResponse(int returnCode) {
+ sendMessage(EVENT_DNS_NOTIFICATION, returnCode);
+ }
+
+ /**
* Send a notification to NetworkMonitor indicating that private DNS settings have changed.
* @param newCfg The new private DNS configuration.
*/
@@ -411,9 +403,75 @@
sendMessage(CMD_PRIVATE_DNS_SETTINGS_CHANGED, newCfg);
}
+ /**
+ * Send a notification to NetworkMonitor indicating that the system is ready.
+ */
+ public void notifySystemReady() {
+ // No need to run on the handler thread: mSystemReady is volatile and read only once on the
+ // isCaptivePortal() thread.
+ mSystemReady = true;
+ }
+
+ /**
+ * Send a notification to NetworkMonitor indicating that the network is now connected.
+ */
+ public void notifyNetworkConnected() {
+ sendMessage(CMD_NETWORK_CONNECTED);
+ }
+
+ /**
+ * Send a notification to NetworkMonitor indicating that the network is now disconnected.
+ */
+ public void notifyNetworkDisconnected() {
+ sendMessage(CMD_NETWORK_DISCONNECTED);
+ }
+
+ /**
+ * Send a notification to NetworkMonitor indicating that link properties have changed.
+ */
+ public void notifyLinkPropertiesChanged() {
+ getHandler().post(() -> {
+ updateLinkProperties();
+ });
+ }
+
+ private void updateLinkProperties() {
+ final LinkProperties lp = mCm.getLinkProperties(mNetwork);
+ // If null, we should soon get a message that the network was disconnected, and will stop.
+ if (lp != null) {
+ // TODO: send LinkProperties parceled in notifyLinkPropertiesChanged() and start().
+ mLinkProperties = lp;
+ }
+ }
+
+ /**
+ * Send a notification to NetworkMonitor indicating that network capabilities have changed.
+ */
+ public void notifyNetworkCapabilitiesChanged() {
+ getHandler().post(() -> {
+ updateNetworkCapabilities();
+ });
+ }
+
+ private void updateNetworkCapabilities() {
+ final NetworkCapabilities nc = mCm.getNetworkCapabilities(mNetwork);
+ // If null, we should soon get a message that the network was disconnected, and will stop.
+ if (nc != null) {
+ // TODO: send NetworkCapabilities parceled in notifyNetworkCapsChanged() and start().
+ mNetworkCapabilities = nc;
+ }
+ }
+
+ /**
+ * Request the captive portal application to be launched.
+ */
+ public void launchCaptivePortalApp() {
+ sendMessage(CMD_LAUNCH_CAPTIVE_PORTAL_APP);
+ }
+
@Override
protected void log(String s) {
- if (DBG) Log.d(TAG + "/" + mNetworkAgentInfo.name(), s);
+ if (DBG) Log.d(TAG + "/" + mNetwork.netId, s);
}
private void validationLog(int probeType, Object url, String msg) {
@@ -423,11 +481,7 @@
private void validationLog(String s) {
if (DBG) log(s);
- validationLogs.log(s);
- }
-
- public ReadOnlyLocalLog getValidationLogs() {
- return validationLogs.readOnlyLocalLog();
+ mValidationLogs.log(s);
}
private ValidationStage validationStage() {
@@ -435,20 +489,46 @@
}
private boolean isValidationRequired() {
- return isValidationRequired(
- mDefaultRequest.networkCapabilities, mNetworkAgentInfo.networkCapabilities);
+ return NetworkMonitorUtils.isValidationRequired(
+ mDefaultRequest.networkCapabilities, mNetworkCapabilities);
}
- private void notifyNetworkTestResultInvalid(Object obj) {
- mConnectivityServiceHandler.sendMessage(obtainMessage(
- EVENT_NETWORK_TESTED, NETWORK_TEST_RESULT_INVALID, mNetId, obj));
+ private void notifyNetworkTested(int result, @Nullable String redirectUrl) {
+ try {
+ mCallback.notifyNetworkTested(result, redirectUrl);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error sending network test result", e);
+ }
+ }
+
+ private void showProvisioningNotification(String action) {
+ try {
+ mCallback.showProvisioningNotification(action);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error showing provisioning notification", e);
+ }
+ }
+
+ private void hideProvisioningNotification() {
+ try {
+ mCallback.hideProvisioningNotification();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error hiding provisioning notification", e);
+ }
}
// DefaultState is the parent of all States. It exists only to handle CMD_* messages but
// does not entail any real state (hence no enter() or exit() routines).
private class DefaultState extends State {
@Override
+ public void enter() {
+ // TODO: have those passed parceled in start() and remove this
+ updateLinkProperties();
+ updateNetworkCapabilities();
+ }
+
+ @Override
public boolean processMessage(Message message) {
switch (message.what) {
case CMD_NETWORK_CONNECTED:
@@ -499,7 +579,7 @@
case APP_RETURN_UNWANTED:
mDontDisplaySigninNotification = true;
mUserDoesNotWant = true;
- notifyNetworkTestResultInvalid(null);
+ notifyNetworkTested(NETWORK_TEST_RESULT_INVALID, null);
// TODO: Should teardown network.
mUidResponsibleForReeval = 0;
transitionTo(mEvaluatingState);
@@ -563,8 +643,7 @@
public void enter() {
maybeLogEvaluationResult(
networkEventType(validationStage(), EvaluationResult.VALIDATED));
- mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED,
- NETWORK_TEST_RESULT_VALID, mNetId, null));
+ notifyNetworkTested(INetworkMonitor.NETWORK_TEST_RESULT_VALID, null);
mValidations++;
}
@@ -633,8 +712,7 @@
@Override
public void exit() {
- Message message = obtainMessage(EVENT_PROVISIONING_NOTIFICATION, 0, mNetId, null);
- mConnectivityServiceHandler.sendMessage(message);
+ hideProvisioningNotification();
}
}
@@ -751,9 +829,7 @@
CMD_LAUNCH_CAPTIVE_PORTAL_APP);
}
// Display the sign in notification.
- Message message = obtainMessage(EVENT_PROVISIONING_NOTIFICATION, 1, mNetId,
- mLaunchCaptivePortalAppBroadcastReceiver.getPendingIntent());
- mConnectivityServiceHandler.sendMessage(message);
+ showProvisioningNotification(mLaunchCaptivePortalAppBroadcastReceiver.mAction);
// Retest for captive portal occasionally.
sendMessageDelayed(CMD_CAPTIVE_PORTAL_RECHECK, 0 /* no UID */,
CAPTIVE_PORTAL_REEVALUATE_DELAY_MS);
@@ -839,12 +915,15 @@
}
private void notifyPrivateDnsConfigResolved() {
- mConnectivityServiceHandler.sendMessage(obtainMessage(
- EVENT_PRIVATE_DNS_CONFIG_RESOLVED, 0, mNetId, mPrivateDnsConfig));
+ try {
+ mCallback.notifyPrivateDnsConfigResolved(mPrivateDnsConfig.toParcel());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error sending private DNS config resolved notification", e);
+ }
}
private void handlePrivateDnsEvaluationFailure() {
- notifyNetworkTestResultInvalid(null);
+ notifyNetworkTested(NETWORK_TEST_RESULT_INVALID, null);
// Queue up a re-evaluation with backoff.
//
@@ -865,7 +944,7 @@
+ oneTimeHostnameSuffix;
final Stopwatch watch = new Stopwatch().start();
try {
- final InetAddress[] ips = mNetworkAgentInfo.network().getAllByName(host);
+ final InetAddress[] ips = mNonPrivateDnsBypassNetwork.getAllByName(host);
final long time = watch.stop();
final String strIps = Arrays.toString(ips);
final boolean success = (ips != null && ips.length > 0);
@@ -915,12 +994,12 @@
// state (even if no Private DNS validation required).
transitionTo(mEvaluatingPrivateDnsState);
} else if (probeResult.isPortal()) {
- notifyNetworkTestResultInvalid(probeResult.redirectUrl);
+ notifyNetworkTested(NETWORK_TEST_RESULT_INVALID, probeResult.redirectUrl);
mLastPortalProbeResult = probeResult;
transitionTo(mCaptivePortalState);
} else {
logNetworkEvent(NetworkEvent.NETWORK_VALIDATION_FAILED);
- notifyNetworkTestResultInvalid(probeResult.redirectUrl);
+ notifyNetworkTested(NETWORK_TEST_RESULT_INVALID, probeResult.redirectUrl);
transitionTo(mWaitingForNextProbeState);
}
return HANDLED;
@@ -996,18 +1075,18 @@
}
}
- public boolean getIsCaptivePortalCheckEnabled() {
+ private boolean getIsCaptivePortalCheckEnabled() {
String symbol = Settings.Global.CAPTIVE_PORTAL_MODE;
int defaultValue = Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT;
int mode = mDependencies.getSetting(mContext, symbol, defaultValue);
return mode != Settings.Global.CAPTIVE_PORTAL_MODE_IGNORE;
}
- public boolean getUseHttpsValidation() {
+ private boolean getUseHttpsValidation() {
return mDependencies.getSetting(mContext, Settings.Global.CAPTIVE_PORTAL_USE_HTTPS, 1) == 1;
}
- public boolean getWifiScansAlwaysAvailableDisabled() {
+ private boolean getWifiScansAlwaysAvailableDisabled() {
return mDependencies.getSetting(
mContext, Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0) == 0;
}
@@ -1040,15 +1119,6 @@
DEFAULT_DATA_STALL_EVALUATION_TYPES);
}
- // Static for direct access by ConnectivityService
- public static String getCaptivePortalServerHttpUrl(Context context) {
- return getCaptivePortalServerHttpUrl(Dependencies.DEFAULT, context);
- }
-
- public static String getCaptivePortalServerHttpUrl(Dependencies deps, Context context) {
- return deps.getSetting(context, Settings.Global.CAPTIVE_PORTAL_HTTP_URL, DEFAULT_HTTP_URL);
- }
-
private URL[] makeCaptivePortalFallbackUrls() {
try {
String separator = ",";
@@ -1144,7 +1214,7 @@
// 3. PAC scripts are sometimes used to block or restrict Internet access and may in
// fact block fetching of the generate_204 URL which would lead to false negative
// results for network validation.
- final ProxyInfo proxyInfo = mNetworkAgentInfo.linkProperties.getHttpProxy();
+ final ProxyInfo proxyInfo = mLinkProperties.getHttpProxy();
if (proxyInfo != null && !Uri.EMPTY.equals(proxyInfo.getPacFileUrl())) {
pacUrl = makeURL(proxyInfo.getPacFileUrl().toString());
if (pacUrl == null) {
@@ -1416,89 +1486,86 @@
return;
}
- if (!systemReady) {
+ if (!mSystemReady) {
return;
}
Intent latencyBroadcast =
- new Intent(ConnectivityConstants.ACTION_NETWORK_CONDITIONS_MEASURED);
- switch (mNetworkAgentInfo.networkInfo.getType()) {
- case ConnectivityManager.TYPE_WIFI:
- WifiInfo currentWifiInfo = mWifiManager.getConnectionInfo();
- if (currentWifiInfo != null) {
- // NOTE: getSSID()'s behavior changed in API 17; before that, SSIDs were not
- // surrounded by double quotation marks (thus violating the Javadoc), but this
- // was changed to match the Javadoc in API 17. Since clients may have started
- // sanitizing the output of this method since API 17 was released, we should
- // not change it here as it would become impossible to tell whether the SSID is
- // simply being surrounded by quotes due to the API, or whether those quotes
- // are actually part of the SSID.
- latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_SSID,
- currentWifiInfo.getSSID());
- latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_BSSID,
- currentWifiInfo.getBSSID());
- } else {
- if (VDBG) logw("network info is TYPE_WIFI but no ConnectionInfo found");
- return;
- }
- break;
- case ConnectivityManager.TYPE_MOBILE:
- latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_NETWORK_TYPE,
- mTelephonyManager.getNetworkType());
- List<CellInfo> info = mTelephonyManager.getAllCellInfo();
- if (info == null) return;
- int numRegisteredCellInfo = 0;
- for (CellInfo cellInfo : info) {
- if (cellInfo.isRegistered()) {
- numRegisteredCellInfo++;
- if (numRegisteredCellInfo > 1) {
- if (VDBG) {
- logw("more than one registered CellInfo."
- + " Can't tell which is active. Bailing.");
- }
- return;
+ new Intent(NetworkMonitorUtils.ACTION_NETWORK_CONDITIONS_MEASURED);
+ if (mNetworkCapabilities.hasTransport(TRANSPORT_WIFI)) {
+ WifiInfo currentWifiInfo = mWifiManager.getConnectionInfo();
+ if (currentWifiInfo != null) {
+ // NOTE: getSSID()'s behavior changed in API 17; before that, SSIDs were not
+ // surrounded by double quotation marks (thus violating the Javadoc), but this
+ // was changed to match the Javadoc in API 17. Since clients may have started
+ // sanitizing the output of this method since API 17 was released, we should
+ // not change it here as it would become impossible to tell whether the SSID is
+ // simply being surrounded by quotes due to the API, or whether those quotes
+ // are actually part of the SSID.
+ latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_SSID,
+ currentWifiInfo.getSSID());
+ latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_BSSID,
+ currentWifiInfo.getBSSID());
+ } else {
+ if (VDBG) logw("network info is TYPE_WIFI but no ConnectionInfo found");
+ return;
+ }
+ latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CONNECTIVITY_TYPE, TYPE_WIFI);
+ } else if (mNetworkCapabilities.hasTransport(TRANSPORT_CELLULAR)) {
+ latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_NETWORK_TYPE,
+ mTelephonyManager.getNetworkType());
+ List<CellInfo> info = mTelephonyManager.getAllCellInfo();
+ if (info == null) return;
+ int numRegisteredCellInfo = 0;
+ for (CellInfo cellInfo : info) {
+ if (cellInfo.isRegistered()) {
+ numRegisteredCellInfo++;
+ if (numRegisteredCellInfo > 1) {
+ if (VDBG) {
+ logw("more than one registered CellInfo."
+ + " Can't tell which is active. Bailing.");
}
- if (cellInfo instanceof CellInfoCdma) {
- CellIdentityCdma cellId = ((CellInfoCdma) cellInfo).getCellIdentity();
- latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_CELL_ID, cellId);
- } else if (cellInfo instanceof CellInfoGsm) {
- CellIdentityGsm cellId = ((CellInfoGsm) cellInfo).getCellIdentity();
- latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_CELL_ID, cellId);
- } else if (cellInfo instanceof CellInfoLte) {
- CellIdentityLte cellId = ((CellInfoLte) cellInfo).getCellIdentity();
- latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_CELL_ID, cellId);
- } else if (cellInfo instanceof CellInfoWcdma) {
- CellIdentityWcdma cellId = ((CellInfoWcdma) cellInfo).getCellIdentity();
- latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_CELL_ID, cellId);
- } else {
- if (VDBG) logw("Registered cellinfo is unrecognized");
- return;
- }
+ return;
+ }
+ if (cellInfo instanceof CellInfoCdma) {
+ CellIdentityCdma cellId = ((CellInfoCdma) cellInfo).getCellIdentity();
+ latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CELL_ID, cellId);
+ } else if (cellInfo instanceof CellInfoGsm) {
+ CellIdentityGsm cellId = ((CellInfoGsm) cellInfo).getCellIdentity();
+ latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CELL_ID, cellId);
+ } else if (cellInfo instanceof CellInfoLte) {
+ CellIdentityLte cellId = ((CellInfoLte) cellInfo).getCellIdentity();
+ latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CELL_ID, cellId);
+ } else if (cellInfo instanceof CellInfoWcdma) {
+ CellIdentityWcdma cellId = ((CellInfoWcdma) cellInfo).getCellIdentity();
+ latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CELL_ID, cellId);
+ } else {
+ if (VDBG) logw("Registered cellinfo is unrecognized");
+ return;
}
}
- break;
- default:
- return;
+ }
+ latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CONNECTIVITY_TYPE, TYPE_MOBILE);
+ } else {
+ return;
}
- latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_CONNECTIVITY_TYPE,
- mNetworkAgentInfo.networkInfo.getType());
- latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_RESPONSE_RECEIVED,
+ latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_RESPONSE_RECEIVED,
responseReceived);
- latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_REQUEST_TIMESTAMP_MS,
+ latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_REQUEST_TIMESTAMP_MS,
requestTimestampMs);
if (responseReceived) {
- latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_IS_CAPTIVE_PORTAL,
+ latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_IS_CAPTIVE_PORTAL,
isCaptivePortal);
- latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_RESPONSE_TIMESTAMP_MS,
+ latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_RESPONSE_TIMESTAMP_MS,
responseTimestampMs);
}
mContext.sendBroadcastAsUser(latencyBroadcast, UserHandle.CURRENT,
- ConnectivityConstants.PERMISSION_ACCESS_NETWORK_CONDITIONS);
+ NetworkMonitorUtils.PERMISSION_ACCESS_NETWORK_CONDITIONS);
}
private void logNetworkEvent(int evtype) {
- int[] transports = mNetworkAgentInfo.networkCapabilities.getTransportTypes();
+ int[] transports = mNetworkCapabilities.getTransportTypes();
mMetricsLog.log(mNetId, transports, new NetworkEvent(evtype));
}
@@ -1520,14 +1587,14 @@
private void maybeLogEvaluationResult(int evtype) {
if (mEvaluationTimer.isRunning()) {
- int[] transports = mNetworkAgentInfo.networkCapabilities.getTransportTypes();
+ int[] transports = mNetworkCapabilities.getTransportTypes();
mMetricsLog.log(mNetId, transports, new NetworkEvent(evtype, mEvaluationTimer.stop()));
mEvaluationTimer.reset();
}
}
private void logValidationProbe(long durationMs, int probeType, int probeResult) {
- int[] transports = mNetworkAgentInfo.networkCapabilities.getTransportTypes();
+ int[] transports = mNetworkCapabilities.getTransportTypes();
boolean isFirstValidation = validationStage().mIsFirstValidation;
ValidationProbeEvent ev = new ValidationProbeEvent();
ev.probeType = ValidationProbeEvent.makeProbeType(probeType, isFirstValidation);
@@ -1537,9 +1604,9 @@
}
@VisibleForTesting
- public static class Dependencies {
- public Network getNetwork(NetworkAgentInfo networkAgentInfo) {
- return new OneAddressPerFamilyNetwork(networkAgentInfo.network());
+ static class Dependencies {
+ public Network getPrivateDnsBypassNetwork(Network network) {
+ return new OneAddressPerFamilyNetwork(network);
}
public Random getRandom() {
@@ -1547,6 +1614,13 @@
}
/**
+ * Get the captive portal server HTTP URL that is configured on the device.
+ */
+ public String getCaptivePortalServerHttpUrl(Context context) {
+ return NetworkMonitorUtils.getCaptivePortalServerHttpUrl(context);
+ }
+
+ /**
* Get the value of a global integer setting.
* @param symbol Name of the setting
* @param defaultValue Value to return if the setting is not defined.
@@ -1666,7 +1740,7 @@
boolean result = false;
// Reevaluation will generate traffic. Thus, set a minimal reevaluation timer to limit the
// possible traffic cost in metered network.
- if (mNetworkAgentInfo.networkCapabilities.isMetered()
+ if (mNetworkCapabilities.isMetered()
&& (SystemClock.elapsedRealtime() - getLastProbeTime()
< mDataStallMinEvaluateTime)) {
return false;
diff --git a/tests/net/java/com/android/server/connectivity/NetworkMonitorTest.java b/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
similarity index 68%
rename from tests/net/java/com/android/server/connectivity/NetworkMonitorTest.java
rename to packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
index 6e07b26..d31fa77 100644
--- a/tests/net/java/com/android/server/connectivity/NetworkMonitorTest.java
+++ b/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
@@ -16,6 +16,14 @@
package com.android.server.connectivity;
+import static android.net.ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN;
+import static android.net.ConnectivityManager.EXTRA_CAPTIVE_PORTAL;
+import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_INVALID;
+import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_VALID;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
+
+import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -24,13 +32,21 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
+import android.content.Intent;
+import android.net.CaptivePortal;
import android.net.ConnectivityManager;
+import android.net.INetworkMonitorCallbacks;
+import android.net.InetAddresses;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
@@ -38,9 +54,12 @@
import android.net.NetworkRequest;
import android.net.captiveportal.CaptivePortalProbeResult;
import android.net.metrics.IpConnectivityLog;
+import android.net.util.SharedLog;
import android.net.wifi.WifiManager;
+import android.os.ConditionVariable;
import android.os.Handler;
import android.os.SystemClock;
+import android.os.UserHandle;
import android.provider.Settings;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -50,8 +69,10 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
import java.io.IOException;
import java.net.HttpURLConnection;
@@ -68,21 +89,23 @@
private static final String LOCATION_HEADER = "location";
private @Mock Context mContext;
- private @Mock Handler mHandler;
private @Mock IpConnectivityLog mLogger;
- private @Mock NetworkAgentInfo mAgent;
- private @Mock NetworkAgentInfo mNotMeteredAgent;
+ private @Mock SharedLog mValidationLogger;
private @Mock NetworkInfo mNetworkInfo;
- private @Mock NetworkRequest mRequest;
+ private @Mock ConnectivityManager mCm;
private @Mock TelephonyManager mTelephony;
private @Mock WifiManager mWifi;
- private @Mock Network mNetwork;
private @Mock HttpURLConnection mHttpConnection;
private @Mock HttpURLConnection mHttpsConnection;
private @Mock HttpURLConnection mFallbackConnection;
private @Mock HttpURLConnection mOtherFallbackConnection;
private @Mock Random mRandom;
private @Mock NetworkMonitor.Dependencies mDependencies;
+ private @Mock INetworkMonitorCallbacks mCallbacks;
+ private @Spy Network mNetwork = new Network(TEST_NETID);
+ private NetworkRequest mRequest;
+
+ private static final int TEST_NETID = 4242;
private static final String TEST_HTTP_URL = "http://www.google.com/gen_204";
private static final String TEST_HTTPS_URL = "https://www.google.com/gen_204";
@@ -93,33 +116,37 @@
private static final int RETURN_CODE_DNS_SUCCESS = 0;
private static final int RETURN_CODE_DNS_TIMEOUT = 255;
+ private static final int HANDLER_TIMEOUT_MS = 1000;
+
+ private static final LinkProperties TEST_LINKPROPERTIES = new LinkProperties();
+
+ private static final NetworkCapabilities METERED_CAPABILITIES = new NetworkCapabilities()
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+ .addCapability(NET_CAPABILITY_INTERNET);
+
+ private static final NetworkCapabilities NOT_METERED_CAPABILITIES = new NetworkCapabilities()
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+ .addCapability(NET_CAPABILITY_INTERNET)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
+
+ private static final NetworkCapabilities NO_INTERNET_CAPABILITIES = new NetworkCapabilities()
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
+
@Before
public void setUp() throws IOException {
MockitoAnnotations.initMocks(this);
- mAgent.linkProperties = new LinkProperties();
- mAgent.networkCapabilities = new NetworkCapabilities()
- .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
- mAgent.networkInfo = mNetworkInfo;
-
- mNotMeteredAgent.linkProperties = new LinkProperties();
- mNotMeteredAgent.networkCapabilities = new NetworkCapabilities()
- .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
- mNotMeteredAgent.networkInfo = mNetworkInfo;
-
- when(mAgent.network()).thenReturn(mNetwork);
- when(mDependencies.getNetwork(any())).thenReturn(mNetwork);
+ when(mDependencies.getPrivateDnsBypassNetwork(any())).thenReturn(mNetwork);
when(mDependencies.getRandom()).thenReturn(mRandom);
when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_MODE), anyInt()))
.thenReturn(Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT);
when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_USE_HTTPS),
anyInt())).thenReturn(1);
- when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_HTTP_URL),
- anyString())).thenReturn(TEST_HTTP_URL);
+ when(mDependencies.getCaptivePortalServerHttpUrl(any())).thenReturn(TEST_HTTP_URL);
when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_HTTPS_URL),
anyString())).thenReturn(TEST_HTTPS_URL);
- when(mNetwork.getPrivateDnsBypassingCopy()).thenReturn(mNetwork);
+ doReturn(mNetwork).when(mNetwork).getPrivateDnsBypassingCopy();
+ when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn(mCm);
when(mContext.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephony);
when(mContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mWifi);
@@ -129,7 +156,7 @@
setFallbackSpecs(null); // Test with no fallback spec by default
when(mRandom.nextInt()).thenReturn(0);
- when(mNetwork.openConnection(any())).then((invocation) -> {
+ doAnswer((invocation) -> {
URL url = invocation.getArgument(0);
switch(url.toString()) {
case TEST_HTTP_URL:
@@ -144,12 +171,20 @@
fail("URL not mocked: " + url.toString());
return null;
}
- });
+ }).when(mNetwork).openConnection(any());
when(mHttpConnection.getRequestProperties()).thenReturn(new ArrayMap<>());
when(mHttpsConnection.getRequestProperties()).thenReturn(new ArrayMap<>());
- when(mNetwork.getAllByName(any())).thenReturn(new InetAddress[] {
- InetAddress.parseNumericAddress("192.168.0.0")
- });
+ doReturn(new InetAddress[] {
+ InetAddresses.parseNumericAddress("192.168.0.0")
+ }).when(mNetwork).getAllByName(any());
+
+ mRequest = new NetworkRequest.Builder()
+ .addCapability(NET_CAPABILITY_INTERNET)
+ .addCapability(NET_CAPABILITY_NOT_RESTRICTED)
+ .build();
+ // Default values. Individual tests can override these.
+ when(mCm.getLinkProperties(any())).thenReturn(TEST_LINKPROPERTIES);
+ when(mCm.getNetworkCapabilities(any())).thenReturn(METERED_CAPABILITIES);
setMinDataStallEvaluateInterval(500);
setDataStallEvaluationType(1 << DATA_STALL_EVALUATION_TYPE_DNS);
@@ -160,10 +195,10 @@
private class WrappedNetworkMonitor extends NetworkMonitor {
private long mProbeTime = 0;
- WrappedNetworkMonitor(Context context, Handler handler,
- NetworkAgentInfo networkAgentInfo, NetworkRequest defaultRequest,
+ WrappedNetworkMonitor(Context context, Network network, NetworkRequest defaultRequest,
IpConnectivityLog logger, Dependencies deps) {
- super(context, handler, networkAgentInfo, defaultRequest, logger, deps);
+ super(context, mCallbacks, network, defaultRequest, logger,
+ new SharedLog("test_nm"), deps);
}
@Override
@@ -176,19 +211,39 @@
}
}
- WrappedNetworkMonitor makeMeteredWrappedNetworkMonitor() {
- return new WrappedNetworkMonitor(
- mContext, mHandler, mAgent, mRequest, mLogger, mDependencies);
+ private WrappedNetworkMonitor makeMeteredWrappedNetworkMonitor() {
+ final WrappedNetworkMonitor nm = new WrappedNetworkMonitor(
+ mContext, mNetwork, mRequest, mLogger, mDependencies);
+ when(mCm.getNetworkCapabilities(any())).thenReturn(METERED_CAPABILITIES);
+ nm.start();
+ waitForIdle(nm.getHandler());
+ return nm;
}
- WrappedNetworkMonitor makeNotMeteredWrappedNetworkMonitor() {
- return new WrappedNetworkMonitor(
- mContext, mHandler, mNotMeteredAgent, mRequest, mLogger, mDependencies);
+ private WrappedNetworkMonitor makeNotMeteredWrappedNetworkMonitor() {
+ final WrappedNetworkMonitor nm = new WrappedNetworkMonitor(
+ mContext, mNetwork, mRequest, mLogger, mDependencies);
+ when(mCm.getNetworkCapabilities(any())).thenReturn(NOT_METERED_CAPABILITIES);
+ nm.start();
+ waitForIdle(nm.getHandler());
+ return nm;
}
- NetworkMonitor makeMonitor() {
- return new NetworkMonitor(
- mContext, mHandler, mAgent, mRequest, mLogger, mDependencies);
+ private NetworkMonitor makeMonitor() {
+ final NetworkMonitor nm = new NetworkMonitor(
+ mContext, mCallbacks, mNetwork, mRequest, mLogger, mValidationLogger,
+ mDependencies);
+ nm.start();
+ waitForIdle(nm.getHandler());
+ return nm;
+ }
+
+ private void waitForIdle(Handler handler) {
+ final ConditionVariable cv = new ConditionVariable(false);
+ handler.post(cv::open);
+ if (!cv.block(HANDLER_TIMEOUT_MS)) {
+ fail("Timed out waiting for handler");
+ }
}
@Test
@@ -319,6 +374,15 @@
}
@Test
+ public void testIsCaptivePortal_IgnorePortals() throws IOException {
+ setCaptivePortalMode(Settings.Global.CAPTIVE_PORTAL_MODE_IGNORE);
+ setSslException(mHttpsConnection);
+ setPortal302(mHttpConnection);
+
+ assertNotPortal(makeMonitor().isCaptivePortal());
+ }
+
+ @Test
public void testIsDataStall_EvaluationDisabled() {
setDataStallEvaluationType(0);
WrappedNetworkMonitor wrappedMonitor = makeMeteredWrappedNetworkMonitor();
@@ -390,6 +454,63 @@
assertFalse(wrappedMonitor.isDataStall());
}
+ @Test
+ public void testBrokenNetworkNotValidated() throws Exception {
+ setSslException(mHttpsConnection);
+ setStatus(mHttpConnection, 500);
+ setStatus(mFallbackConnection, 404);
+ when(mCm.getNetworkCapabilities(any())).thenReturn(METERED_CAPABILITIES);
+
+ final NetworkMonitor nm = makeMonitor();
+ nm.notifyNetworkConnected();
+
+ verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1))
+ .notifyNetworkTested(NETWORK_TEST_RESULT_INVALID, null);
+ }
+
+ @Test
+ public void testNoInternetCapabilityValidated() throws Exception {
+ when(mCm.getNetworkCapabilities(any())).thenReturn(NO_INTERNET_CAPABILITIES);
+
+ final NetworkMonitor nm = makeMonitor();
+ nm.notifyNetworkConnected();
+
+ verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1))
+ .notifyNetworkTested(NETWORK_TEST_RESULT_VALID, null);
+ verify(mNetwork, never()).openConnection(any());
+ }
+
+ @Test
+ public void testLaunchCaptivePortalApp() throws Exception {
+ setSslException(mHttpsConnection);
+ setPortal302(mHttpConnection);
+
+ final NetworkMonitor nm = makeMonitor();
+ nm.notifyNetworkConnected();
+
+ verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1))
+ .showProvisioningNotification(any());
+
+ // Check that startCaptivePortalApp sends the expected intent.
+ nm.launchCaptivePortalApp();
+
+ final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext, timeout(HANDLER_TIMEOUT_MS).times(1))
+ .startActivityAsUser(intentCaptor.capture(), eq(UserHandle.CURRENT));
+ final Intent intent = intentCaptor.getValue();
+ assertEquals(ACTION_CAPTIVE_PORTAL_SIGN_IN, intent.getAction());
+ final Network network = intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK);
+ assertEquals(TEST_NETID, network.netId);
+
+ // Have the app report that the captive portal is dismissed, and check that we revalidate.
+ setStatus(mHttpsConnection, 204);
+ setStatus(mHttpConnection, 204);
+ final CaptivePortal captivePortal = intent.getParcelableExtra(EXTRA_CAPTIVE_PORTAL);
+ captivePortal.reportCaptivePortalDismissed();
+ verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1))
+ .notifyNetworkTested(NETWORK_TEST_RESULT_VALID, null);
+ }
+
private void makeDnsTimeoutEvent(WrappedNetworkMonitor wrappedMonitor, int count) {
for (int i = 0; i < count; i++) {
wrappedMonitor.getDnsStallDetector().accumulateConsecutiveDnsTimeoutCount(
@@ -440,6 +561,11 @@
eq(Settings.Global.CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS), any())).thenReturn(specs);
}
+ private void setCaptivePortalMode(int mode) {
+ when(mDependencies.getSetting(any(),
+ eq(Settings.Global.CAPTIVE_PORTAL_MODE), anyInt())).thenReturn(mode);
+ }
+
private void assertPortal(CaptivePortalProbeResult result) {
assertTrue(result.isPortal());
assertFalse(result.isFailed());
@@ -459,12 +585,12 @@
}
private void setSslException(HttpURLConnection connection) throws IOException {
- when(connection.getResponseCode()).thenThrow(new SSLHandshakeException("Invalid cert"));
+ doThrow(new SSLHandshakeException("Invalid cert")).when(connection).getResponseCode();
}
private void set302(HttpURLConnection connection, String location) throws IOException {
setStatus(connection, 302);
- when(connection.getHeaderField(LOCATION_HEADER)).thenReturn(location);
+ doReturn(location).when(connection).getHeaderField(LOCATION_HEADER);
}
private void setPortal302(HttpURLConnection connection) throws IOException {
@@ -472,7 +598,7 @@
}
private void setStatus(HttpURLConnection connection, int status) throws IOException {
- when(connection.getResponseCode()).thenReturn(status);
+ doReturn(status).when(connection).getResponseCode();
}
}
diff --git a/packages/SystemUI/res/layout/super_status_bar.xml b/packages/SystemUI/res/layout/super_status_bar.xml
index 34c208a..02062bb 100644
--- a/packages/SystemUI/res/layout/super_status_bar.xml
+++ b/packages/SystemUI/res/layout/super_status_bar.xml
@@ -43,6 +43,14 @@
android:visibility="invisible" />
</com.android.systemui.statusbar.BackDropView>
+ <com.android.systemui.wallpaper.AodMaskView
+ android:id="@+id/aod_mask"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:importantForAccessibility="no"
+ android:visibility="invisible"
+ sysui:ignoreRightInset="true" />
+
<com.android.systemui.statusbar.ScrimView
android:id="@+id/scrim_behind"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 59838d2..633f868 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -109,5 +109,10 @@
<!-- Optional cancel button on Keyguard -->
<item type="id" name="cancel_button"/>
+
+ <!-- AodMaskView transition tag -->
+ <item type="id" name="aod_mask_transition_progress_tag" />
+ <item type="id" name="aod_mask_transition_progress_end_tag" />
+ <item type="id" name="aod_mask_transition_progress_start_tag" />
</resources>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 1c8a672..17cc1d5 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -55,6 +55,10 @@
* Consumer that accepts the a new ClockPlugin implementation when the Extension reloads.
*/
private final Consumer<ClockPlugin> mClockPluginConsumer = plugin -> setClockPlugin(plugin);
+ /**
+ * Maintain state so that a newly connected plugin can be initialized.
+ */
+ private float mDarkAmount;
private final StatusBarStateController.StateListener mStateListener =
new StatusBarStateController.StateListener() {
@@ -147,6 +151,7 @@
mClockPlugin = plugin;
mClockPlugin.setStyle(getPaint().getStyle());
mClockPlugin.setTextColor(getCurrentTextColor());
+ mClockPlugin.setDarkAmount(mDarkAmount);
}
/**
@@ -208,6 +213,7 @@
* @param darkAmount Amount of transition to doze: 1f for doze and 0f for awake.
*/
public void setDarkAmount(float darkAmount) {
+ mDarkAmount = darkAmount;
if (mClockPlugin != null) {
mClockPlugin.setDarkAmount(darkAmount);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
index 280dda0..577e8d6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
@@ -84,6 +84,14 @@
public void onCancelled() {
pulseFinished();
}
+
+ /**
+ * Whether to fade out wallpaper.
+ */
+ @Override
+ public boolean isFadeOutWallpaper() {
+ return mPulseReason == DozeLog.PULSE_REASON_DOCKING;
+ }
};
public DozeScrimController(DozeParameters dozeParameters) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 4bece48..853d7ab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -275,9 +275,12 @@
holdWakeLock();
}
- // AOD wallpapers should fade away after a while
- if (mWallpaperSupportsAmbientMode && mDozeParameters.getAlwaysOn()
- && mState == ScrimState.AOD) {
+ // AOD wallpapers should fade away after a while.
+ // Docking pulses may take a long time, wallpapers should also fade away after a while.
+ if (mWallpaperSupportsAmbientMode && (
+ mDozeParameters.getAlwaysOn() && mState == ScrimState.AOD
+ || mState == ScrimState.PULSING && mCallback != null
+ && mCallback.isFadeOutWallpaper())) {
if (!mWallpaperVisibilityTimedOut) {
mTimeTicker.schedule(mDozeParameters.getWallpaperAodDuration(),
AlarmTimeout.MODE_IGNORE_IF_SCHEDULED);
@@ -329,7 +332,7 @@
@VisibleForTesting
protected void onHideWallpaperTimeout() {
- if (mState != ScrimState.AOD) {
+ if (mState != ScrimState.AOD && mState != ScrimState.PULSING) {
return;
}
@@ -504,7 +507,8 @@
// We want to override the back scrim opacity for the AOD state
// when it's time to fade the wallpaper away.
- boolean aodWallpaperTimeout = mState == ScrimState.AOD && mWallpaperVisibilityTimedOut;
+ boolean aodWallpaperTimeout = (mState == ScrimState.AOD || mState == ScrimState.PULSING)
+ && mWallpaperVisibilityTimedOut;
// We also want to hide FLAG_SHOW_WHEN_LOCKED activities under the scrim.
boolean occludedKeyguard = (mState == ScrimState.PULSING || mState == ScrimState.AOD)
&& mKeyguardOccluded;
@@ -917,6 +921,9 @@
}
default void onCancelled() {
}
+ default boolean isFadeOutWallpaper() {
+ return false;
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index fb3c4aa..72519ba3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -219,6 +219,14 @@
public void prepare(ScrimState previousState) {
}
+ /**
+ * Check if lockscreen wallpaper or music album art exists.
+ * @return true if lockscreen wallpaper or music album art exists.
+ */
+ public boolean hasBackdrop() {
+ return mHasBackdrop;
+ }
+
public int getIndex() {
return mIndex;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 6f877ba..9abd86d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -70,6 +70,7 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.database.ContentObserver;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
@@ -98,6 +99,7 @@
import android.service.notification.StatusBarNotification;
import android.util.DisplayMetrics;
import android.util.EventLog;
+import android.util.FeatureFlagUtils;
import android.util.Log;
import android.util.Slog;
import android.view.Display;
@@ -478,8 +480,13 @@
WallpaperInfo info = wallpaperManager.getWallpaperInfo(UserHandle.USER_CURRENT);
final boolean deviceSupportsAodWallpaper = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_dozeSupportsAodWallpaper);
+ final boolean aodImageWallpaperEnabled = FeatureFlagUtils.isEnabled(mContext,
+ FeatureFlagUtils.AOD_IMAGEWALLPAPER_ENABLED);
+ updateAodMaskVisibility(deviceSupportsAodWallpaper && aodImageWallpaperEnabled);
+ // If WallpaperInfo is null, it must be ImageWallpaper.
final boolean supportsAmbientMode = deviceSupportsAodWallpaper
- && info != null && info.supportsAmbientMode();
+ && (info == null && aodImageWallpaperEnabled
+ || info != null && info.supportsAmbientMode());
mStatusBarWindowController.setWallpaperSupportsAmbientMode(supportsAmbientMode);
mScrimController.setWallpaperSupportsAmbientMode(supportsAmbientMode);
@@ -581,6 +588,7 @@
protected NotificationPresenter mPresenter;
private NotificationActivityStarter mNotificationActivityStarter;
private boolean mPulsing;
+ private ContentObserver mFeatureFlagObserver;
@Override
public void onActiveStateChanged(int code, int uid, String packageName, boolean active) {
@@ -697,6 +705,9 @@
mContext.registerReceiverAsUser(mWallpaperChangedReceiver, UserHandle.ALL,
wallpaperChangedFilter, null /* broadcastPermission */, null /* scheduler */);
mWallpaperChangedReceiver.onReceive(mContext, null);
+ mFeatureFlagObserver = new FeatureFlagObserver(
+ FeatureFlagUtils.AOD_IMAGEWALLPAPER_ENABLED /* feature */,
+ () -> mWallpaperChangedReceiver.onReceive(mContext, null) /* callback */);
// Set up the initial notification state. This needs to happen before CommandQueue.disable()
setUpPresenter();
@@ -4415,4 +4426,33 @@
public @TransitionMode int getStatusBarMode() {
return mStatusBarMode;
}
+
+ private void updateAodMaskVisibility(boolean supportsAodWallpaper) {
+ View mask = mStatusBarWindow.findViewById(R.id.aod_mask);
+ if (mask != null) {
+ mask.setVisibility(supportsAodWallpaper ? View.VISIBLE : View.INVISIBLE);
+ }
+ }
+
+ private final class FeatureFlagObserver extends ContentObserver {
+ private final Runnable mCallback;
+
+ FeatureFlagObserver(String feature, Runnable callback) {
+ this(null, feature, callback);
+ }
+
+ private FeatureFlagObserver(Handler handler, String feature, Runnable callback) {
+ super(handler);
+ mCallback = callback;
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Global.getUriFor(feature), false, this);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ if (mCallback != null) {
+ mStatusBarWindow.post(mCallback);
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/wallpaper/AodMaskView.java b/packages/SystemUI/src/com/android/systemui/wallpaper/AodMaskView.java
new file mode 100644
index 0000000..52cabe2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/wallpaper/AodMaskView.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.wallpaper;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.app.WallpaperManager;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.RectF;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManager.DisplayListener;
+import android.util.AttributeSet;
+import android.util.FeatureFlagUtils;
+import android.util.Log;
+import android.view.Display;
+import android.view.DisplayInfo;
+import android.widget.ImageView;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.Dependency;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.notification.AnimatableProperty;
+import com.android.systemui.statusbar.notification.PropertyAnimator;
+import com.android.systemui.statusbar.notification.stack.AnimationProperties;
+import com.android.systemui.statusbar.phone.ScrimState;
+
+/**
+ * A view that draws mask upon either image wallpaper or music album art in AOD.
+ */
+public class AodMaskView extends ImageView implements StatusBarStateController.StateListener,
+ ImageWallpaperTransformer.TransformationListener {
+ private static final String TAG = AodMaskView.class.getSimpleName();
+ private static final int TRANSITION_DURATION = 1000;
+
+ private static final AnimatableProperty TRANSITION_PROGRESS = AnimatableProperty.from(
+ "transition_progress",
+ AodMaskView::setTransitionAmount,
+ AodMaskView::getTransitionAmount,
+ R.id.aod_mask_transition_progress_tag,
+ R.id.aod_mask_transition_progress_start_tag,
+ R.id.aod_mask_transition_progress_end_tag
+ );
+
+ private final AnimationProperties mTransitionProperties = new AnimationProperties();
+ private final ImageWallpaperTransformer mTransformer;
+ private final RectF mBounds = new RectF();
+ private boolean mChangingStates;
+ private boolean mNeedMask;
+ private float mTransitionAmount;
+ private final WallpaperManager mWallpaperManager;
+ private final DisplayManager mDisplayManager;
+ private DisplayListener mDisplayListener = new DisplayListener() {
+ @Override
+ public void onDisplayAdded(int displayId) {
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ }
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+ // We just support DEFAULT_DISPLAY currently.
+ if (displayId == Display.DEFAULT_DISPLAY) {
+ mTransformer.updateDisplayInfo(getDisplayInfo(displayId));
+ }
+ }
+ };
+
+ public AodMaskView(Context context) {
+ this(context, null);
+ }
+
+ public AodMaskView(Context context, AttributeSet attrs) {
+ this(context, attrs, null);
+ }
+
+ @VisibleForTesting
+ public AodMaskView(Context context, AttributeSet attrs, ImageWallpaperTransformer transformer) {
+ super(context, attrs);
+ setClickable(false);
+
+ StatusBarStateController controller = Dependency.get(StatusBarStateController.class);
+ if (controller != null) {
+ controller.addCallback(this);
+ } else {
+ Log.d(TAG, "Can not get StatusBarStateController!");
+ }
+
+ mDisplayManager = (DisplayManager) getContext().getSystemService(Context.DISPLAY_SERVICE);
+ mDisplayManager.registerDisplayListener(mDisplayListener, null);
+ mWallpaperManager =
+ (WallpaperManager) getContext().getSystemService(Context.WALLPAPER_SERVICE);
+
+ if (transformer == null) {
+ mTransformer = new ImageWallpaperTransformer(this);
+ mTransformer.addFilter(new ScrimFilter());
+ mTransformer.addFilter(new VignetteFilter());
+ mTransformer.updateOffsets();
+ mTransformer.updateDisplayInfo(getDisplayInfo(Display.DEFAULT_DISPLAY));
+
+ mTransitionProperties.setDuration(TRANSITION_DURATION);
+ mTransitionProperties.setAnimationFinishListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mTransformer.setIsTransiting(false);
+ }
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mTransformer.setIsTransiting(true);
+ }
+ });
+ } else {
+ // This part should only be hit by test cases.
+ mTransformer = transformer;
+ }
+ }
+
+ private DisplayInfo getDisplayInfo(int displayId) {
+ DisplayInfo displayInfo = new DisplayInfo();
+ mDisplayManager.getDisplay(displayId).getDisplayInfo(displayInfo);
+ return displayInfo;
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+ mBounds.set(0, 0, w, h);
+ mTransformer.updateOffsets();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ if (mNeedMask) {
+ mTransformer.drawTransformedImage(canvas, null /* target */, null /* src */, mBounds);
+ }
+ }
+
+ private boolean checkIfNeedMask() {
+ // We need mask for ImageWallpaper / LockScreen Wallpaper (Music album art).
+ return mWallpaperManager.getWallpaperInfo() == null || ScrimState.AOD.hasBackdrop();
+ }
+
+ @Override
+ public void onStatePreChange(int oldState, int newState) {
+ mChangingStates = oldState != newState;
+ mNeedMask = checkIfNeedMask();
+ }
+
+ @Override
+ public void onStatePostChange() {
+ mChangingStates = false;
+ }
+
+ @Override
+ public void onStateChanged(int newState) {
+ }
+
+ @Override
+ public void onDozingChanged(boolean isDozing) {
+ if (!mNeedMask) {
+ return;
+ }
+
+ boolean enabled = checkFeatureIsEnabled();
+ mTransformer.updateAmbientModeState(enabled && isDozing);
+
+ if (enabled && !mChangingStates) {
+ setAnimatorProperty(isDozing);
+ } else {
+ invalidate();
+ }
+ }
+
+ private boolean checkFeatureIsEnabled() {
+ return FeatureFlagUtils.isEnabled(
+ getContext(), FeatureFlagUtils.AOD_IMAGEWALLPAPER_ENABLED);
+ }
+
+ @VisibleForTesting
+ void setAnimatorProperty(boolean isDozing) {
+ PropertyAnimator.setProperty(
+ this,
+ TRANSITION_PROGRESS,
+ isDozing ? 1f : 0f /* newEndValue */,
+ mTransitionProperties,
+ true /* animated */);
+ }
+
+ @Override
+ public void onTransformationUpdated() {
+ invalidate();
+ }
+
+ private void setTransitionAmount(float amount) {
+ mTransitionAmount = amount;
+ mTransformer.updateTransitionAmount(amount);
+ }
+
+ private float getTransitionAmount() {
+ return mTransitionAmount;
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/wallpaper/ImageWallpaperFilter.java b/packages/SystemUI/src/com/android/systemui/wallpaper/ImageWallpaperFilter.java
new file mode 100644
index 0000000..d457dac
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/wallpaper/ImageWallpaperFilter.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.wallpaper;
+
+import android.animation.ValueAnimator;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.RectF;
+
+/**
+ * Abstract filter used by static image wallpaper.
+ */
+abstract class ImageWallpaperFilter {
+ protected static final boolean DEBUG = false;
+
+ private ImageWallpaperTransformer mTransformer;
+
+ /**
+ * Apply this filter to the bitmap before drawing on canvas.
+ * @param c The canvas that will draw to.
+ * @param bitmap The bitmap to apply this filter.
+ * @param src The subset of the bitmap to be drawn.
+ * @param dest The rectangle that the bitmap will be scaled/translated to fit into.
+ */
+ public abstract void apply(@NonNull Canvas c, @Nullable Bitmap bitmap,
+ @Nullable Rect src, @NonNull RectF dest);
+
+ /**
+ * Notifies the occurrence of built-in transition of the animation.
+ * @param animator The animator which was animated.
+ */
+ public abstract void onAnimatorUpdate(ValueAnimator animator);
+
+ /**
+ * Notifies the occurrence of another transition of the animation.
+ * @param amount The transition amount.
+ */
+ public abstract void onTransitionAmountUpdate(float amount);
+
+ /**
+ * To set the associated transformer.
+ * @param transformer The transformer that is associated with this filter.
+ */
+ public void setTransformer(ImageWallpaperTransformer transformer) {
+ if (transformer != null) {
+ mTransformer = transformer;
+ }
+ }
+
+ protected ImageWallpaperTransformer getTransformer() {
+ return mTransformer;
+ }
+
+ /**
+ * Notifies the changing of the offset value of the ImageWallpaper.
+ * @param force True to force re-evaluate offsets.
+ * @param xOffset X offset of the ImageWallpaper in percentage.
+ * @param yOffset Y offset of the ImageWallpaper in percentage.
+ */
+ public void onOffsetsUpdate(boolean force, float xOffset, float yOffset) {
+ // No-op
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/wallpaper/ImageWallpaperTransformer.java b/packages/SystemUI/src/com/android/systemui/wallpaper/ImageWallpaperTransformer.java
new file mode 100644
index 0000000..25b0b0a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/wallpaper/ImageWallpaperTransformer.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.wallpaper;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.view.DisplayInfo;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class is used to manage the filters that will be applied.
+ */
+public class ImageWallpaperTransformer {
+ private static final String TAG = ImageWallpaperTransformer.class.getSimpleName();
+
+ private DisplayInfo mDisplayInfo;
+ private final List<ImageWallpaperFilter> mFilters;
+ private final TransformationListener mListener;
+ private boolean mIsInAmbientMode;
+ private boolean mIsTransiting;
+
+ /**
+ * Constructor.
+ * @param listener A listener to inform you the transformation has updated.
+ */
+ public ImageWallpaperTransformer(TransformationListener listener) {
+ mFilters = new ArrayList<>();
+ mListener = listener;
+ }
+
+ /**
+ * Claim that we want to use the specified filter.
+ * @param filter The filter will be used.
+ */
+ public void addFilter(ImageWallpaperFilter filter) {
+ if (filter != null) {
+ filter.setTransformer(this);
+ mFilters.add(filter);
+ }
+ }
+
+ /**
+ * Check if any transition is running.
+ * @return True if the transition is running, false otherwise.
+ */
+ boolean isTransiting() {
+ return mIsTransiting;
+ }
+
+ /**
+ * Indicate if any transition is running. <br/>
+ * @param isTransiting True if the transition is running.
+ */
+ void setIsTransiting(boolean isTransiting) {
+ mIsTransiting = isTransiting;
+ }
+
+ /**
+ * Check if the device is in ambient mode.
+ * @return True if the device is in ambient mode, false otherwise.
+ */
+ public boolean isInAmbientMode() {
+ return mIsInAmbientMode;
+ }
+
+ /**
+ * Update current state of ambient mode.
+ * @param isInAmbientMode Current ambient mode state.
+ */
+ public void updateAmbientModeState(boolean isInAmbientMode) {
+ mIsInAmbientMode = isInAmbientMode;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ int idx = 0;
+ for (ImageWallpaperFilter filter : mFilters) {
+ sb.append(idx++).append(": ").append(filter.getClass().getSimpleName()).append("\n");
+ }
+ if (sb.length() == 0) {
+ sb.append("No filters applied");
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Set a new display info.
+ * @param displayInfo New display info.
+ */
+ public void updateDisplayInfo(DisplayInfo displayInfo) {
+ mDisplayInfo = displayInfo;
+ }
+
+ /**
+ * To get current display info.
+ * @return Current display info.
+ */
+ public DisplayInfo getDisplayInfo() {
+ return mDisplayInfo;
+ }
+
+ /**
+ * Update the offsets with default value.
+ */
+ public void updateOffsets() {
+ this.updateOffsets(true, 0f, .5f);
+ }
+
+ /**
+ * To notify the filters that the offset of the ImageWallpaper changes.
+ * @param force True to force re-evaluate offsets.
+ * @param offsetX X offset of the ImageWallpaper in percentage.
+ * @param offsetY Y offset of the ImageWallpaper in percentage.
+ */
+ public void updateOffsets(boolean force, float offsetX, float offsetY) {
+ mFilters.forEach(filter -> filter.onOffsetsUpdate(force, offsetX, offsetY));
+ }
+
+ /**
+ * Apply all specified filters to the bitmap then draw to the canvas.
+ * @param c The canvas that will draw to.
+ * @param target The bitmap to apply filters.
+ * @param src The subset of the bitmap to be drawn
+ * @param dest The rectangle that the bitmap will be scaled/translated to fit into.
+ */
+ void drawTransformedImage(@NonNull Canvas c, @Nullable Bitmap target,
+ @Nullable Rect src, @NonNull RectF dest) {
+ mFilters.forEach(filter -> filter.apply(c, target, src, dest));
+ }
+
+ /**
+ * Update the transition amount. <br/>
+ * Must invoke this to update transition amount if not running built-in transition.
+ * @param amount The transition amount.
+ */
+ void updateTransitionAmount(float amount) {
+ mFilters.forEach(filter -> filter.onTransitionAmountUpdate(amount));
+ if (mListener != null) {
+ mListener.onTransformationUpdated();
+ }
+ }
+
+ /**
+ * An interface that informs the transformation status.
+ */
+ public interface TransformationListener {
+ /**
+ * Notifies the update of the transformation.
+ */
+ void onTransformationUpdated();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/wallpaper/ScrimFilter.java b/packages/SystemUI/src/com/android/systemui/wallpaper/ScrimFilter.java
new file mode 100644
index 0000000..637e48e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/wallpaper/ScrimFilter.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.wallpaper;
+
+import android.animation.ValueAnimator;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.RectF;
+
+/**
+ * A filter that implements 70% black scrim effect.
+ */
+public class ScrimFilter extends ImageWallpaperFilter {
+ private static final int MAX_ALPHA = (int) (255 * .7f);
+ private static final int MIN_ALPHA = 0;
+
+ private final Paint mPaint;
+
+ public ScrimFilter() {
+ mPaint = new Paint();
+ mPaint.setColor(Color.BLACK);
+ mPaint.setAlpha(MAX_ALPHA);
+ }
+
+ @Override
+ public void apply(Canvas c, Bitmap bitmap, Rect src, RectF dest) {
+ ImageWallpaperTransformer transformer = getTransformer();
+
+ // If it is not in the transition, we need to set the property according to aod state.
+ if (!transformer.isTransiting()) {
+ mPaint.setAlpha(transformer.isInAmbientMode() ? MAX_ALPHA : MIN_ALPHA);
+ }
+
+ c.drawRect(dest, mPaint);
+ }
+
+ @Override
+ public void onAnimatorUpdate(ValueAnimator animator) {
+ ImageWallpaperTransformer transformer = getTransformer();
+ float fraction = animator.getAnimatedFraction();
+ float factor = transformer.isInAmbientMode() ? fraction : 1f - fraction;
+ mPaint.setAlpha((int) (factor * MAX_ALPHA));
+ }
+
+ @Override
+ public void onTransitionAmountUpdate(float amount) {
+ mPaint.setAlpha((int) (amount * MAX_ALPHA));
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/wallpaper/VignetteFilter.java b/packages/SystemUI/src/com/android/systemui/wallpaper/VignetteFilter.java
new file mode 100644
index 0000000..ad0b98b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/wallpaper/VignetteFilter.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.wallpaper;
+
+import android.animation.ValueAnimator;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.PointF;
+import android.graphics.RadialGradient;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Shader;
+import android.util.Log;
+import android.view.DisplayInfo;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * A filter that implements vignette effect.
+ */
+public class VignetteFilter extends ImageWallpaperFilter {
+ private static final String TAG = VignetteFilter.class.getSimpleName();
+ private static final int MAX_ALPHA = 255;
+ private static final int MIN_ALPHA = 0;
+
+ private final Paint mPaint;
+ private final Matrix mMatrix;
+ private final Shader mShader;
+
+ private float mXOffset;
+ private float mYOffset;
+ private float mCenterX;
+ private float mCenterY;
+ private float mStretchX;
+ private float mStretchY;
+ private boolean mCalculateOffsetNeeded;
+
+ public VignetteFilter() {
+ mPaint = new Paint();
+ mMatrix = new Matrix();
+ mShader = new RadialGradient(0, 0, 1,
+ Color.TRANSPARENT, Color.BLACK, Shader.TileMode.CLAMP);
+ }
+
+ @Override
+ public void apply(Canvas c, Bitmap bitmap, Rect src, RectF dest) {
+ DisplayInfo info = getTransformer().getDisplayInfo();
+
+ if (mCalculateOffsetNeeded) {
+ int lw = info.logicalWidth;
+ int lh = info.logicalHeight;
+ mCenterX = lw / 2 + (dest.width() - lw) * mXOffset;
+ mCenterY = lh / 2 + (dest.height() - lh) * mYOffset;
+ mStretchX = info.logicalWidth / 2;
+ mStretchY = info.logicalHeight / 2;
+ mCalculateOffsetNeeded = false;
+ }
+
+ if (DEBUG) {
+ Log.d(TAG, "apply: lw=" + info.logicalWidth + ", lh=" + info.logicalHeight
+ + ", center=(" + mCenterX + "," + mCenterY + ")"
+ + ", stretch=(" + mStretchX + "," + mStretchY + ")");
+ }
+
+ mMatrix.reset();
+ mMatrix.postTranslate(mCenterX, mCenterY);
+ mMatrix.postScale(mStretchX, mStretchY, mCenterX, mCenterY);
+ mShader.setLocalMatrix(mMatrix);
+ mPaint.setShader(mShader);
+
+ ImageWallpaperTransformer transformer = getTransformer();
+
+ // If it is not in the transition, we need to set the property according to aod state.
+ if (!transformer.isTransiting()) {
+ mPaint.setAlpha(transformer.isInAmbientMode() ? MAX_ALPHA : MIN_ALPHA);
+ }
+
+ c.drawRect(dest, mPaint);
+ }
+
+ @Override
+ public void onAnimatorUpdate(ValueAnimator animator) {
+ ImageWallpaperTransformer transformer = getTransformer();
+ float fraction = animator.getAnimatedFraction();
+ float factor = transformer.isInAmbientMode() ? fraction : 1f - fraction;
+ mPaint.setAlpha((int) (factor * MAX_ALPHA));
+ }
+
+ @Override
+ public void onTransitionAmountUpdate(float amount) {
+ mPaint.setAlpha((int) (amount * MAX_ALPHA));
+ }
+
+ @Override
+ public void onOffsetsUpdate(boolean force, float xOffset, float yOffset) {
+ if (force || mXOffset != xOffset || mYOffset != yOffset) {
+ mXOffset = xOffset;
+ mYOffset = yOffset;
+ mCalculateOffsetNeeded = true;
+ }
+ }
+
+ @VisibleForTesting
+ public PointF getCenterPoint() {
+ return new PointF(mCenterX, mCenterY);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
index 8e02f57..fbc1c20 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
@@ -132,6 +132,17 @@
}
@Test
+ public void onPluginConnected_darkAmountInitialized() {
+ // GIVEN that the dark amount has already been set
+ mKeyguardClockSwitch.setDarkAmount(0.5f);
+ // WHEN a plugin is connected
+ ClockPlugin plugin = mock(ClockPlugin.class);
+ mKeyguardClockSwitch.getClockPluginConsumer().accept(plugin);
+ // THEN dark amount should be initalized on the plugin.
+ verify(plugin).setDarkAmount(0.5f);
+ }
+
+ @Test
public void onPluginDisconnected_showDefaultClock() {
ClockPlugin plugin = mock(ClockPlugin.class);
TextClock pluginView = new TextClock(getContext());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java b/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java
index 521d5d1..53ad0b5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java
@@ -16,17 +16,12 @@
package com.android.systemui;
-
-import static org.junit.Assert.assertNotNull;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.graphics.Bitmap;
@@ -58,13 +53,15 @@
@Mock private SurfaceHolder mSurfaceHolder;
@Mock private DisplayInfo mDisplayInfo;
- CountDownLatch mEventCountdown;
+ private CountDownLatch mEventCountdown;
+ private CountDownLatch mAmbientEventCountdown;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mEventCountdown = new CountDownLatch(1);
+ mAmbientEventCountdown = new CountDownLatch(2);
mImageWallpaper = new ImageWallpaper() {
@Override
@@ -86,6 +83,11 @@
assertTrue("mFixedSizeAllowed should be true", allowed);
mEventCountdown.countDown();
}
+
+ @Override
+ public void onAmbientModeChanged(boolean inAmbientMode, long duration) {
+ mAmbientEventCountdown.countDown();
+ }
};
}
};
@@ -132,4 +134,23 @@
verify(mSurfaceHolder, times(1)).setFixedSize(ImageWallpaper.DrawableEngine.MIN_BACKGROUND_WIDTH, ImageWallpaper.DrawableEngine.MIN_BACKGROUND_HEIGHT);
}
+ @Test
+ public void testDeliversAmbientModeChanged() {
+ ImageWallpaper.DrawableEngine wallpaperEngine =
+ (ImageWallpaper.DrawableEngine) mImageWallpaper.onCreateEngine();
+
+ assertEquals("setFixedSizeAllowed should have been called.",
+ 0, mEventCountdown.getCount());
+
+ wallpaperEngine.setCreated(true);
+ wallpaperEngine.doAmbientModeChanged(false, 1000);
+ assertFalse("ambient mode should be false", wallpaperEngine.isInAmbientMode());
+ assertEquals("onAmbientModeChanged should have been called.",
+ 1, mAmbientEventCountdown.getCount());
+
+ wallpaperEngine.doAmbientModeChanged(true, 1000);
+ assertTrue("ambient mode should be true", wallpaperEngine.isInAmbientMode());
+ assertEquals("onAmbientModeChanged should have been called.",
+ 0, mAmbientEventCountdown.getCount());
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index cc5f50a..8eb42c4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -94,6 +94,7 @@
},
visible -> mScrimVisibility = visible, mDozeParamenters, mAlarmManager);
mScrimController.setHasBackdrop(false);
+ mScrimController.setWallpaperSupportsAmbientMode(false);
}
@Test
@@ -474,6 +475,26 @@
}
@Test
+ public void testHoldsPulsingWallpaperAnimationLock() {
+ // Pre-conditions
+ mScrimController.transitionTo(ScrimState.PULSING, new ScrimController.Callback() {
+ @Override
+ public boolean isFadeOutWallpaper() {
+ return true;
+ }
+ });
+ mScrimController.finishAnimationsImmediately();
+ reset(mWakeLock);
+
+ mScrimController.onHideWallpaperTimeout();
+ verify(mWakeLock).acquire();
+ verify(mWakeLock, never()).release();
+ mScrimController.finishAnimationsImmediately();
+ verify(mWakeLock).release();
+ assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_OPAQUE);
+ }
+
+ @Test
public void testWillHideAodWallpaper() {
mScrimController.setWallpaperSupportsAmbientMode(true);
mScrimController.transitionTo(ScrimState.AOD);
@@ -483,6 +504,34 @@
}
@Test
+ public void testWillHidePulsingWallpaper_withRequestFadeOut() {
+ mScrimController.setWallpaperSupportsAmbientMode(true);
+ mScrimController.transitionTo(ScrimState.PULSING, new ScrimController.Callback() {
+ @Override
+ public boolean isFadeOutWallpaper() {
+ return true;
+ }
+ });
+ verify(mAlarmManager).setExact(anyInt(), anyLong(), any(), any(), any());
+ mScrimController.transitionTo(ScrimState.KEYGUARD);
+ verify(mAlarmManager).cancel(any(AlarmManager.OnAlarmListener.class));
+ }
+
+ @Test
+ public void testDoesNotHidePulsingWallpaper_withoutRequestFadeOut() {
+ mScrimController.setWallpaperSupportsAmbientMode(true);
+ mScrimController.transitionTo(ScrimState.PULSING, new ScrimController.Callback() {});
+ verify(mAlarmManager, never()).setExact(anyInt(), anyLong(), any(), any(), any());
+ }
+
+ @Test
+ public void testDoesNotHidePulsingWallpaper_withoutCallback() {
+ mScrimController.setWallpaperSupportsAmbientMode(true);
+ mScrimController.transitionTo(ScrimState.PULSING);
+ verify(mAlarmManager, never()).setExact(anyInt(), anyLong(), any(), any(), any());
+ }
+
+ @Test
public void testConservesExpansionOpacityAfterTransition() {
mScrimController.transitionTo(ScrimState.UNLOCKED);
mScrimController.setPanelExpansion(0.5f);
@@ -738,5 +787,4 @@
callback.run();
}
}
-
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallpaper/AodMaskViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallpaper/AodMaskViewTest.java
new file mode 100644
index 0000000..c44a366
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallpaper/AodMaskViewTest.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.wallpaper;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.WallpaperManager;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.RectF;
+import android.hardware.display.DisplayManager;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.FeatureFlagUtils;
+import android.view.DisplayInfo;
+import android.view.WindowManager;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AodMaskViewTest extends SysuiTestCase {
+ private AodMaskView mMaskView;
+ private DisplayInfo mDisplayInfo;
+ private ImageWallpaperTransformer mTransformer;
+
+ @Before
+ public void setUp() throws Exception {
+ DisplayManager displayManager =
+ spy((DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE));
+ doNothing().when(displayManager).registerDisplayListener(any(), any());
+ mContext.addMockSystemService(DisplayManager.class, displayManager);
+
+ WallpaperManager wallpaperManager =
+ spy((WallpaperManager) mContext.getSystemService(Context.WALLPAPER_SERVICE));
+ doReturn(null).when(wallpaperManager).getWallpaperInfo();
+ mContext.addMockSystemService(WallpaperManager.class, wallpaperManager);
+
+ mTransformer = spy(new ImageWallpaperTransformer(null /* listener */));
+ mMaskView = spy(new AodMaskView(getContext(), null /* attrs */, mTransformer));
+ mDisplayInfo = new DisplayInfo();
+
+ ((WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE))
+ .getDefaultDisplay().getDisplayInfo(mDisplayInfo);
+
+ FeatureFlagUtils.setEnabled(
+ mContext, FeatureFlagUtils.AOD_IMAGEWALLPAPER_ENABLED, true);
+ }
+
+ @After
+ public void tearDown() {
+ FeatureFlagUtils.setEnabled(
+ mContext, FeatureFlagUtils.AOD_IMAGEWALLPAPER_ENABLED, false);
+ }
+
+ @Test
+ public void testCreateMaskView_TransformerIsNotNull() {
+ assertNotNull("mTransformer should not be null", mTransformer);
+ }
+
+ @Test
+ public void testAodMaskView_ShouldNotClickable() {
+ assertFalse("MaskView should not be clickable", mMaskView.isClickable());
+ }
+
+ @Test
+ public void testAodMaskView_OnSizeChange_ShouldUpdateTransformerOffsets() {
+ mMaskView.onSizeChanged(mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight, 0, 0);
+ verify(mTransformer, times(1)).updateOffsets();
+ }
+
+ @Test
+ public void testAodMaskView_OnDraw_ShouldDrawTransformedImage() {
+ Canvas c = new Canvas();
+ RectF bounds = new RectF(0, 0, mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
+ mMaskView.onSizeChanged(mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight, 0, 0);
+ mMaskView.onStatePreChange(0, 1);
+ mMaskView.onDraw(c);
+ verify(mTransformer, times(1)).drawTransformedImage(c, null, null, bounds);
+ }
+
+ @Test
+ public void testAodMaskView_IsDozing_ShouldUpdateAmbientModeState() {
+ doNothing().when(mMaskView).setAnimatorProperty(anyBoolean());
+ mMaskView.onStatePreChange(0, 1);
+ mMaskView.onDozingChanged(true);
+ verify(mTransformer, times(1)).updateAmbientModeState(true);
+ }
+
+ @Test
+ public void testAodMaskView_IsDozing_ShouldDoTransitionOrDrawFinalFrame() {
+ doNothing().when(mMaskView).setAnimatorProperty(anyBoolean());
+ mMaskView.onStatePreChange(0, 1);
+ mMaskView.onDozingChanged(true);
+ mMaskView.onStatePostChange();
+ mMaskView.onDozingChanged(false);
+ verify(mMaskView, times(1)).invalidate();
+ verify(mMaskView, times(1)).setAnimatorProperty(false);
+ }
+
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallpaper/ImageWallpaperTransformerTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallpaper/ImageWallpaperTransformerTest.java
new file mode 100644
index 0000000..55b0aae
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallpaper/ImageWallpaperTransformerTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.wallpaper;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.PointF;
+import android.graphics.RectF;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.view.DisplayInfo;
+import android.view.WindowManager;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ImageWallpaperTransformerTest extends SysuiTestCase {
+ private DisplayInfo mDisplayInfo;
+ private Bitmap mBitmap;
+ private Canvas mCanvas;
+ private RectF mDestination;
+
+ @Before
+ public void setUp() throws Exception {
+ mDisplayInfo = new DisplayInfo();
+ ((WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE))
+ .getDefaultDisplay().getDisplayInfo(mDisplayInfo);
+ int dimension = Math.max(mDisplayInfo.logicalHeight, mDisplayInfo.logicalWidth);
+ mBitmap = Bitmap.createBitmap(dimension, dimension, Bitmap.Config.ARGB_8888);
+ mCanvas = new Canvas(mBitmap);
+ mCanvas.drawColor(Color.RED);
+ mDestination = new RectF(0, 0, mBitmap.getWidth(), mBitmap.getHeight());
+ }
+
+ @Test
+ public void testVignetteFilter() {
+ VignetteFilter vignette = new VignetteFilter();
+
+ ImageWallpaperTransformer transformer = getTransformer(vignette);
+ transformer.drawTransformedImage(mCanvas, mBitmap, null, mDestination);
+
+ PointF center = vignette.getCenterPoint();
+ int p1 = mBitmap.getPixel((int) center.x, (int) center.y);
+ int p2 = mBitmap.getPixel(0, 0);
+ int p3 = mBitmap.getPixel(mBitmap.getWidth() - 1, mBitmap.getHeight() - 1);
+
+ assertThat(p1).isEqualTo(Color.RED);
+ assertThat(p2 | p3).isEqualTo(Color.BLACK);
+ }
+
+ @Test
+ public void testScrimFilter() {
+ getTransformer(new ScrimFilter())
+ .drawTransformedImage(mCanvas, mBitmap, null, mDestination);
+
+ int pixel = mBitmap.getPixel(0, 0);
+
+ // 0xff4d0000 is the result of 70% alpha pre-multiplied which is 0.7*(0,0,0)+0.3*(255,0,0).
+ assertThat(pixel).isEqualTo(0xff4d0000);
+ }
+
+ private ImageWallpaperTransformer getTransformer(ImageWallpaperFilter filter) {
+ ImageWallpaperTransformer transformer = new ImageWallpaperTransformer(null);
+ transformer.addFilter(filter);
+ transformer.updateDisplayInfo(mDisplayInfo);
+ transformer.updateOffsets();
+ transformer.updateAmbientModeState(true);
+ return transformer;
+ }
+}
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index 665773c..aff5e13 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -5325,7 +5325,7 @@
// OS: P
ACCESSIBILITY_VIBRATION = 1292;
- // OPEN: Settings > Accessibility > Vibration > Ring & notification vibration
+ // OPEN: Settings > Accessibility > Vibration > Notification vibration
// CATEGORY: SETTINGS
// OS: P
ACCESSIBILITY_VIBRATION_NOTIFICATION = 1293;
@@ -6763,6 +6763,11 @@
// OS: Q
ACTION_TEXT_CLASSIFIER_ACTIONS_GENERATED = 1619;
+ // OPEN: Settings > Accessibility > Vibration > Ring vibration
+ // CATEGORY: SETTINGS
+ // OS: Q
+ ACCESSIBILITY_VIBRATION_RING = 1620;
+
// ---- End Q Constants, all Q constants go above this line ----
// Add new aosp constants above this line.
diff --git a/services/Android.bp b/services/Android.bp
index 01734f4..0abe53d 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -24,6 +24,7 @@
"services.contentcapture",
"services.coverage",
"services.devicepolicy",
+ "services.ipmemorystore",
"services.midi",
"services.net",
"services.print",
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 66ceae4..d0666b9 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -25,6 +25,7 @@
import static android.net.ConnectivityManager.TYPE_VPN;
import static android.net.ConnectivityManager.getNetworkTypeName;
import static android.net.ConnectivityManager.isNetworkTypeValid;
+import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_VALID;
import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
@@ -37,6 +38,8 @@
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkPolicyManager.RULE_NONE;
import static android.net.NetworkPolicyManager.uidRulesToString;
+import static android.net.NetworkStack.NETWORKSTACK_PACKAGE_NAME;
+import static android.net.shared.NetworkMonitorUtils.isValidationRequired;
import static android.os.Process.INVALID_UID;
import static android.system.OsConstants.IPPROTO_TCP;
import static android.system.OsConstants.IPPROTO_UDP;
@@ -62,6 +65,8 @@
import android.net.INetd;
import android.net.INetdEventCallback;
import android.net.INetworkManagementEventObserver;
+import android.net.INetworkMonitor;
+import android.net.INetworkMonitorCallbacks;
import android.net.INetworkPolicyListener;
import android.net.INetworkPolicyManager;
import android.net.INetworkStatsService;
@@ -79,9 +84,11 @@
import android.net.NetworkQuotaInfo;
import android.net.NetworkRequest;
import android.net.NetworkSpecifier;
+import android.net.NetworkStack;
import android.net.NetworkState;
import android.net.NetworkUtils;
import android.net.NetworkWatchlistManager;
+import android.net.PrivateDnsConfigParcel;
import android.net.ProxyInfo;
import android.net.RouteInfo;
import android.net.UidRange;
@@ -90,12 +97,13 @@
import android.net.metrics.IpConnectivityLog;
import android.net.metrics.NetworkEvent;
import android.net.netlink.InetDiagMessage;
+import android.net.shared.NetworkMonitorUtils;
+import android.net.shared.PrivateDnsConfig;
import android.net.util.MultinetworkPolicyTracker;
import android.net.util.NetdService;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
-import android.os.FileUtils;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
@@ -123,8 +131,8 @@
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.LocalLog;
-import android.util.LocalLog.ReadOnlyLocalLog;
import android.util.Log;
+import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
@@ -149,7 +157,6 @@
import com.android.server.am.BatteryStatsService;
import com.android.server.connectivity.DataConnectionStats;
import com.android.server.connectivity.DnsManager;
-import com.android.server.connectivity.DnsManager.PrivateDnsConfig;
import com.android.server.connectivity.DnsManager.PrivateDnsValidationUpdate;
import com.android.server.connectivity.IpConnectivityMetrics;
import com.android.server.connectivity.KeepaliveTracker;
@@ -158,7 +165,6 @@
import com.android.server.connectivity.MultipathPolicyTracker;
import com.android.server.connectivity.NetworkAgentInfo;
import com.android.server.connectivity.NetworkDiagnostics;
-import com.android.server.connectivity.NetworkMonitor;
import com.android.server.connectivity.NetworkNotificationManager;
import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
import com.android.server.connectivity.PermissionMonitor;
@@ -186,7 +192,6 @@
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
-import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -442,6 +447,43 @@
*/
private static final int EVENT_DATA_SAVER_CHANGED = 40;
+ /**
+ * Event for NetworkMonitor/NetworkAgentInfo to inform ConnectivityService that the network has
+ * been tested.
+ * obj = String representing URL that Internet probe was redirect to, if it was redirected.
+ * arg1 = One of the NETWORK_TESTED_RESULT_* constants.
+ * arg2 = NetID.
+ */
+ public static final int EVENT_NETWORK_TESTED = 41;
+
+ /**
+ * Event for NetworkMonitor/NetworkAgentInfo to inform ConnectivityService that the private DNS
+ * config was resolved.
+ * obj = PrivateDnsConfig
+ * arg2 = netid
+ */
+ public static final int EVENT_PRIVATE_DNS_CONFIG_RESOLVED = 42;
+
+ /**
+ * Request ConnectivityService display provisioning notification.
+ * arg1 = Whether to make the notification visible.
+ * arg2 = NetID.
+ * obj = Intent to be launched when notification selected by user, null if !arg1.
+ */
+ public static final int EVENT_PROVISIONING_NOTIFICATION = 43;
+
+ /**
+ * Argument for {@link #EVENT_PROVISIONING_NOTIFICATION} to indicate that the notification
+ * should be shown.
+ */
+ public static final int PROVISIONING_NOTIFICATION_SHOW = 1;
+
+ /**
+ * Argument for {@link #EVENT_PROVISIONING_NOTIFICATION} to indicate that the notification
+ * should be hidden.
+ */
+ public static final int PROVISIONING_NOTIFICATION_HIDE = 0;
+
private static String eventName(int what) {
return sMagicDecoderRing.get(what, Integer.toString(what));
}
@@ -506,30 +548,6 @@
private long mMaxWakelockDurationMs = 0;
private long mLastWakeLockAcquireTimestamp = 0;
- // Array of <Network,ReadOnlyLocalLogs> tracking network validation and results
- private static final int MAX_VALIDATION_LOGS = 10;
- private static class ValidationLog {
- final Network mNetwork;
- final String mName;
- final ReadOnlyLocalLog mLog;
-
- ValidationLog(Network network, String name, ReadOnlyLocalLog log) {
- mNetwork = network;
- mName = name;
- mLog = log;
- }
- }
- private final ArrayDeque<ValidationLog> mValidationLogs = new ArrayDeque<>(MAX_VALIDATION_LOGS);
-
- private void addValidationLogs(ReadOnlyLocalLog log, Network network, String name) {
- synchronized (mValidationLogs) {
- while (mValidationLogs.size() >= MAX_VALIDATION_LOGS) {
- mValidationLogs.removeLast();
- }
- mValidationLogs.addFirst(new ValidationLog(network, name, log));
- }
- }
-
private final IpConnectivityLog mMetricsLog;
@GuardedBy("mBandwidthRequests")
@@ -1684,7 +1702,11 @@
// caller type. Need to re-factor NetdEventListenerService to allow multiple
// NetworkMonitor registrants.
if (nai != null && nai.satisfies(mDefaultRequest)) {
- nai.networkMonitor.sendMessage(NetworkMonitor.EVENT_DNS_NOTIFICATION, returnCode);
+ try {
+ nai.networkMonitor().notifyDnsResponse(returnCode);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
}
}
};
@@ -2266,17 +2288,6 @@
if (ArrayUtils.contains(args, SHORT_ARG) == false) {
pw.println();
- synchronized (mValidationLogs) {
- pw.println("mValidationLogs (most recent first):");
- for (ValidationLog p : mValidationLogs) {
- pw.println(p.mNetwork + " - " + p.mName);
- pw.increaseIndent();
- p.mLog.dump(fd, pw, args);
- pw.decreaseIndent();
- }
- }
-
- pw.println();
pw.println("mNetworkRequestInfoLogs (most recent first):");
pw.increaseIndent();
mNetworkRequestInfoLogs.reverseDump(fd, pw, args);
@@ -2455,11 +2466,11 @@
switch (msg.what) {
default:
return false;
- case NetworkMonitor.EVENT_NETWORK_TESTED: {
+ case EVENT_NETWORK_TESTED: {
final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(msg.arg2);
if (nai == null) break;
- final boolean valid = (msg.arg1 == NetworkMonitor.NETWORK_TEST_RESULT_VALID);
+ final boolean valid = (msg.arg1 == NETWORK_TEST_RESULT_VALID);
final boolean wasValidated = nai.lastValidated;
final boolean wasDefault = isDefaultNetwork(nai);
@@ -2497,7 +2508,7 @@
}
break;
}
- case NetworkMonitor.EVENT_PROVISIONING_NOTIFICATION: {
+ case EVENT_PROVISIONING_NOTIFICATION: {
final int netId = msg.arg2;
final boolean visible = toBool(msg.arg1);
final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(netId);
@@ -2530,7 +2541,7 @@
}
break;
}
- case NetworkMonitor.EVENT_PRIVATE_DNS_CONFIG_RESOLVED: {
+ case EVENT_PRIVATE_DNS_CONFIG_RESOLVED: {
final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(msg.arg2);
if (nai == null) break;
@@ -2572,8 +2583,61 @@
}
}
+ private class NetworkMonitorCallbacks extends INetworkMonitorCallbacks.Stub {
+ private final NetworkAgentInfo mNai;
+
+ private NetworkMonitorCallbacks(NetworkAgentInfo nai) {
+ mNai = nai;
+ }
+
+ @Override
+ public void onNetworkMonitorCreated(INetworkMonitor networkMonitor) {
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_AGENT,
+ new Pair<>(mNai, networkMonitor)));
+ }
+
+ @Override
+ public void notifyNetworkTested(int testResult, @Nullable String redirectUrl) {
+ mTrackerHandler.sendMessage(mTrackerHandler.obtainMessage(EVENT_NETWORK_TESTED,
+ testResult, mNai.network.netId, redirectUrl));
+ }
+
+ @Override
+ public void notifyPrivateDnsConfigResolved(PrivateDnsConfigParcel config) {
+ mTrackerHandler.sendMessage(mTrackerHandler.obtainMessage(
+ EVENT_PRIVATE_DNS_CONFIG_RESOLVED,
+ 0, mNai.network.netId, PrivateDnsConfig.fromParcel(config)));
+ }
+
+ @Override
+ public void showProvisioningNotification(String action) {
+ final Intent intent = new Intent(action);
+ intent.setPackage(NETWORKSTACK_PACKAGE_NAME);
+
+ final PendingIntent pendingIntent;
+ // Only the system server can register notifications with package "android"
+ final long token = Binder.clearCallingIdentity();
+ try {
+ pendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ mTrackerHandler.sendMessage(mTrackerHandler.obtainMessage(
+ EVENT_PROVISIONING_NOTIFICATION, PROVISIONING_NOTIFICATION_SHOW,
+ mNai.network.netId,
+ pendingIntent));
+ }
+
+ @Override
+ public void hideProvisioningNotification() {
+ mTrackerHandler.sendMessage(mTrackerHandler.obtainMessage(
+ EVENT_PROVISIONING_NOTIFICATION, PROVISIONING_NOTIFICATION_HIDE,
+ mNai.network.netId));
+ }
+ }
+
private boolean networkRequiresValidation(NetworkAgentInfo nai) {
- return NetworkMonitor.isValidationRequired(
+ return isValidationRequired(
mDefaultRequest.networkCapabilities, nai.networkCapabilities);
}
@@ -2603,10 +2667,14 @@
// Internet access and therefore also require validation.
if (!networkRequiresValidation(nai)) return;
- // Notify the NetworkMonitor thread in case it needs to cancel or
+ // Notify the NetworkAgentInfo/NetworkMonitor in case NetworkMonitor needs to cancel or
// schedule DNS resolutions. If a DNS resolution is required the
// result will be sent back to us.
- nai.networkMonitor.notifyPrivateDnsSettingsChanged(cfg);
+ try {
+ nai.networkMonitor().notifyPrivateDnsChanged(cfg.toParcel());
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
// With Private DNS bypass support, we can proceed to update the
// Private DNS config immediately, even if we're in strict mode
@@ -2736,7 +2804,11 @@
// Disable wakeup packet monitoring for each interface.
wakeupModifyInterface(iface, nai.networkCapabilities, false);
}
- nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_DISCONNECTED);
+ try {
+ nai.networkMonitor().notifyNetworkDisconnected();
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
mNetworkAgentInfos.remove(nai.messenger);
nai.maybeStopClat();
synchronized (mNetworkForNetId) {
@@ -3096,7 +3168,11 @@
NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
if (nai == null) return;
if (!nai.networkCapabilities.hasCapability(NET_CAPABILITY_CAPTIVE_PORTAL)) return;
- nai.networkMonitor.sendMessage(NetworkMonitor.CMD_LAUNCH_CAPTIVE_PORTAL_APP);
+ try {
+ nai.networkMonitor().launchCaptivePortalApp();
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
});
}
@@ -3217,6 +3293,11 @@
return mMultinetworkPolicyTracker.getMeteredMultipathPreference();
}
+ @Override
+ public NetworkRequest getDefaultRequest() {
+ return mDefaultRequest;
+ }
+
private class InternalHandler extends Handler {
public InternalHandler(Looper looper) {
super(looper);
@@ -3247,7 +3328,9 @@
break;
}
case EVENT_REGISTER_NETWORK_AGENT: {
- handleRegisterNetworkAgent((NetworkAgentInfo)msg.obj);
+ final Pair<NetworkAgentInfo, INetworkMonitor> arg =
+ (Pair<NetworkAgentInfo, INetworkMonitor>) msg.obj;
+ handleRegisterNetworkAgent(arg.first, arg.second);
break;
}
case EVENT_REGISTER_NETWORK_REQUEST:
@@ -3305,7 +3388,14 @@
}
case EVENT_SYSTEM_READY: {
for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
- nai.networkMonitor.systemReady = true;
+ // Might have been called already in handleRegisterNetworkAgent since
+ // mSystemReady is set before sending EVENT_SYSTEM_READY, but calling
+ // this several times is fine.
+ try {
+ nai.networkMonitor().notifySystemReady();
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
}
mMultipathPolicyTracker.start();
break;
@@ -3577,7 +3667,11 @@
if (isNetworkWithLinkPropertiesBlocked(lp, uid, false)) {
return;
}
- nai.networkMonitor.forceReevaluation(uid);
+ try {
+ nai.networkMonitor().forceReevaluation(uid);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
}
@Override
@@ -4785,27 +4879,49 @@
final NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities);
final NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(),
new Network(reserveNetId()), new NetworkInfo(networkInfo), lp, nc, currentScore,
- mContext, mTrackerHandler, new NetworkMisc(networkMisc), mDefaultRequest, this);
+ mContext, mTrackerHandler, new NetworkMisc(networkMisc), this);
// Make sure the network capabilities reflect what the agent info says.
nai.networkCapabilities = mixInCapabilities(nai, nc);
- synchronized (this) {
- nai.networkMonitor.systemReady = mSystemReady;
- }
final String extraInfo = networkInfo.getExtraInfo();
final String name = TextUtils.isEmpty(extraInfo)
? nai.networkCapabilities.getSSID() : extraInfo;
- addValidationLogs(nai.networkMonitor.getValidationLogs(), nai.network, name);
if (DBG) log("registerNetworkAgent " + nai);
- mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_AGENT, nai));
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mContext.getSystemService(NetworkStack.class)
+ .makeNetworkMonitor(nai.network, name, new NetworkMonitorCallbacks(nai));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ // NetworkAgentInfo registration will finish when the NetworkMonitor is created.
+ // If the network disconnects or sends any other event before that, messages are deferred by
+ // NetworkAgent until nai.asyncChannel.connect(), which will be called when finalizing the
+ // registration.
return nai.network.netId;
}
- private void handleRegisterNetworkAgent(NetworkAgentInfo nai) {
+ private void handleRegisterNetworkAgent(NetworkAgentInfo nai, INetworkMonitor networkMonitor) {
+ nai.onNetworkMonitorCreated(networkMonitor);
if (VDBG) log("Got NetworkAgent Messenger");
mNetworkAgentInfos.put(nai.messenger, nai);
synchronized (mNetworkForNetId) {
mNetworkForNetId.put(nai.network.netId, nai);
}
+ synchronized (this) {
+ if (mSystemReady) {
+ try {
+ networkMonitor.notifySystemReady();
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ try {
+ networkMonitor.start();
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
nai.asyncChannel.connect(mContext, mTrackerHandler, nai.messenger);
NetworkInfo networkInfo = nai.networkInfo;
nai.networkInfo = null;
@@ -4855,6 +4971,11 @@
networkAgent.updateClat(mNMS);
notifyIfacesChangedForNetworkStats();
if (networkAgent.everConnected) {
+ try {
+ networkAgent.networkMonitor().notifyLinkPropertiesChanged();
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_IP_CHANGED);
}
}
@@ -5092,6 +5213,11 @@
// If the requestable capabilities have changed or the score changed, we can't have been
// called by rematchNetworkAndRequests, so it's safe to start a rematch.
rematchAllNetworksAndRequests(nai, oldScore);
+ try {
+ nai.networkMonitor().notifyNetworkCapabilitiesChanged();
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED);
}
@@ -5339,6 +5465,11 @@
}
if (capabilitiesChanged) {
+ try {
+ nai.networkMonitor().notifyNetworkCapabilitiesChanged();
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED);
}
@@ -5739,7 +5870,15 @@
updateLinkProperties(networkAgent, new LinkProperties(networkAgent.linkProperties),
null);
- networkAgent.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_CONNECTED);
+ // Until parceled LinkProperties are sent directly to NetworkMonitor, the connect
+ // command must be sent after updating LinkProperties to maximize chances of
+ // NetworkMonitor seeing the correct LinkProperties when starting.
+ // TODO: pass LinkProperties to the NetworkMonitor in the notifyNetworkConnected call.
+ try {
+ networkAgent.networkMonitor().notifyNetworkConnected();
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
scheduleUnvalidatedPrompt(networkAgent);
if (networkAgent.isVPN()) {
@@ -6020,7 +6159,7 @@
@Override
public String getCaptivePortalServerUrl() {
enforceConnectivityInternalPermission();
- return NetworkMonitor.getCaptivePortalServerHttpUrl(mContext);
+ return NetworkMonitorUtils.getCaptivePortalServerHttpUrl(mContext);
}
@Override
@@ -6113,12 +6252,6 @@
}
@VisibleForTesting
- public NetworkMonitor createNetworkMonitor(Context context, Handler handler,
- NetworkAgentInfo nai, NetworkRequest defaultRequest) {
- return new NetworkMonitor(context, handler, nai, defaultRequest);
- }
-
- @VisibleForTesting
MultinetworkPolicyTracker createMultinetworkPolicyTracker(Context c, Handler h, Runnable r) {
return new MultinetworkPolicyTracker(c, h, r);
}
diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java
index f27d373..8adc416 100644
--- a/services/core/java/com/android/server/PackageWatchdog.java
+++ b/services/core/java/com/android/server/PackageWatchdog.java
@@ -16,6 +16,7 @@
package com.android.server;
+import android.annotation.Nullable;
import android.content.Context;
import android.os.Environment;
import android.os.Handler;
@@ -29,6 +30,7 @@
import android.util.Xml;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.XmlUtils;
@@ -46,8 +48,10 @@
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Iterator;
import java.util.List;
+import java.util.Set;
/**
* Monitors the health of packages on the system and notifies interested observers when packages
@@ -58,7 +62,7 @@
// Duration to count package failures before it resets to 0
private static final int TRIGGER_DURATION_MS = 60000;
// Number of package failures within the duration above before we notify observers
- private static final int TRIGGER_FAILURE_COUNT = 5;
+ static final int TRIGGER_FAILURE_COUNT = 5;
private static final int DB_VERSION = 1;
private static final String TAG_PACKAGE_WATCHDOG = "package-watchdog";
private static final String TAG_PACKAGE = "package";
@@ -75,20 +79,13 @@
// Handler to run package cleanup runnables
private final Handler mTimerHandler;
private final Handler mIoHandler;
- // Contains (observer-name -> external-observer-handle) that have been registered during the
- // current boot.
- // It is populated when observers call #registerHealthObserver and it does not survive reboots.
- @GuardedBy("mLock")
- final ArrayMap<String, PackageHealthObserver> mRegisteredObservers = new ArrayMap<>();
- // Contains (observer-name -> internal-observer-handle) that have ever been registered from
+ // Contains (observer-name -> observer-handle) that have ever been registered from
// previous boots. Observers with all packages expired are periodically pruned.
// It is saved to disk on system shutdown and repouplated on startup so it survives reboots.
@GuardedBy("mLock")
- final ArrayMap<String, ObserverInternal> mAllObservers = new ArrayMap<>();
+ private final ArrayMap<String, ObserverInternal> mAllObservers = new ArrayMap<>();
// File containing the XML data of monitored packages /data/system/package-watchdog.xml
- private final AtomicFile mPolicyFile =
- new AtomicFile(new File(new File(Environment.getDataDirectory(), "system"),
- "package-watchdog.xml"));
+ private final AtomicFile mPolicyFile;
// Runnable to prune monitored packages that have expired
private final Runnable mPackageCleanup;
// Last SystemClock#uptimeMillis a package clean up was executed.
@@ -98,14 +95,32 @@
// 0 if mPackageCleanup not running.
private long mDurationAtLastReschedule;
+ // TODO(zezeozue): Remove redundant context param
private PackageWatchdog(Context context) {
mContext = context;
+ mPolicyFile = new AtomicFile(new File(new File(Environment.getDataDirectory(), "system"),
+ "package-watchdog.xml"));
mTimerHandler = new Handler(Looper.myLooper());
mIoHandler = BackgroundThread.getHandler();
mPackageCleanup = this::rescheduleCleanup;
loadFromFile();
}
+ /**
+ * Creates a PackageWatchdog for testing that uses the same {@code looper} for all handlers
+ * and creates package-watchdog.xml in an apps data directory.
+ */
+ @VisibleForTesting
+ PackageWatchdog(Context context, Looper looper) {
+ mContext = context;
+ mPolicyFile = new AtomicFile(new File(context.getFilesDir(), "package-watchdog.xml"));
+ mTimerHandler = new Handler(looper);
+ mIoHandler = mTimerHandler;
+ mPackageCleanup = this::rescheduleCleanup;
+ loadFromFile();
+ }
+
+
/** Creates or gets singleton instance of PackageWatchdog. */
public static PackageWatchdog getInstance(Context context) {
synchronized (PackageWatchdog.class) {
@@ -124,7 +139,10 @@
*/
public void registerHealthObserver(PackageHealthObserver observer) {
synchronized (mLock) {
- mRegisteredObservers.put(observer.getName(), observer);
+ ObserverInternal internalObserver = mAllObservers.get(observer.getName());
+ if (internalObserver != null) {
+ internalObserver.mRegisteredObserver = observer;
+ }
if (mDurationAtLastReschedule == 0) {
// Nothing running, schedule
rescheduleCleanup();
@@ -143,7 +161,7 @@
* or {@code durationMs} is less than 1
*/
public void startObservingHealth(PackageHealthObserver observer, List<String> packageNames,
- int durationMs) {
+ long durationMs) {
if (packageNames.isEmpty() || durationMs < 1) {
throw new IllegalArgumentException("Observation not started, no packages specified"
+ "or invalid duration");
@@ -180,11 +198,32 @@
public void unregisterHealthObserver(PackageHealthObserver observer) {
synchronized (mLock) {
mAllObservers.remove(observer.getName());
- mRegisteredObservers.remove(observer.getName());
}
saveToFileAsync();
}
+ /**
+ * Returns packages observed by {@code observer}
+ *
+ * @return an empty set if {@code observer} has some packages observerd from a previous boot
+ * but has not registered itself in the current boot to receive notifications. Returns null
+ * if there are no active packages monitored from any boot.
+ */
+ @Nullable
+ public Set<String> getPackages(PackageHealthObserver observer) {
+ synchronized (mLock) {
+ for (int i = 0; i < mAllObservers.size(); i++) {
+ if (observer.getName().equals(mAllObservers.keyAt(i))) {
+ if (observer.equals(mAllObservers.valueAt(i).mRegisteredObserver)) {
+ return mAllObservers.valueAt(i).mPackages.keySet();
+ }
+ return Collections.emptySet();
+ }
+ }
+ }
+ return null;
+ }
+
// TODO(zezeozue:) Accept current versionCodes of failing packages?
/**
* Called when a process fails either due to a crash or ANR.
@@ -198,24 +237,23 @@
public void onPackageFailure(String[] packages) {
ArrayMap<String, List<PackageHealthObserver>> packagesToReport = new ArrayMap<>();
synchronized (mLock) {
- if (mRegisteredObservers.isEmpty()) {
+ if (mAllObservers.isEmpty()) {
return;
}
for (int pIndex = 0; pIndex < packages.length; pIndex++) {
+ // Observers interested in receiving packageName failures
+ List<PackageHealthObserver> observersToNotify = new ArrayList<>();
for (int oIndex = 0; oIndex < mAllObservers.size(); oIndex++) {
- // Observers interested in receiving packageName failures
- List<PackageHealthObserver> observersToNotify = new ArrayList<>();
- PackageHealthObserver activeObserver =
- mRegisteredObservers.get(mAllObservers.valueAt(oIndex).mName);
- if (activeObserver != null) {
- observersToNotify.add(activeObserver);
+ PackageHealthObserver registeredObserver =
+ mAllObservers.valueAt(oIndex).mRegisteredObserver;
+ if (registeredObserver != null) {
+ observersToNotify.add(registeredObserver);
}
-
- // Save interested observers and notify them outside the lock
- if (!observersToNotify.isEmpty()) {
- packagesToReport.put(packages[pIndex], observersToNotify);
- }
+ }
+ // Save interested observers and notify them outside the lock
+ if (!observersToNotify.isEmpty()) {
+ packagesToReport.put(packages[pIndex], observersToNotify);
}
}
}
@@ -223,8 +261,11 @@
// Notify observers
for (int pIndex = 0; pIndex < packagesToReport.size(); pIndex++) {
List<PackageHealthObserver> observers = packagesToReport.valueAt(pIndex);
+ String packageName = packages[pIndex];
for (int oIndex = 0; oIndex < observers.size(); oIndex++) {
- if (observers.get(oIndex).onHealthCheckFailed(packages[pIndex])) {
+ PackageHealthObserver observer = observers.get(oIndex);
+ if (mAllObservers.get(observer.getName()).onPackageFailure(packageName)
+ && observer.onHealthCheckFailed(packageName)) {
// Observer has handled, do not notify others
break;
}
@@ -275,10 +316,12 @@
// O if mPackageCleanup not running
long elapsedDurationMs = mUptimeAtLastRescheduleMs == 0
? 0 : uptimeMs - mUptimeAtLastRescheduleMs;
- // O if mPackageCleanup not running
+ // Less than O if mPackageCleanup unexpectedly didn't run yet even though
+ // and we are past the last duration scheduled to run
long remainingDurationMs = mDurationAtLastReschedule - elapsedDurationMs;
-
- if (mUptimeAtLastRescheduleMs == 0 || nextDurationToScheduleMs < remainingDurationMs) {
+ if (mUptimeAtLastRescheduleMs == 0
+ || remainingDurationMs <= 0
+ || nextDurationToScheduleMs < remainingDurationMs) {
// First schedule or an earlier reschedule
pruneObservers(elapsedDurationMs);
mTimerHandler.removeCallbacks(mPackageCleanup);
@@ -305,6 +348,7 @@
}
}
Slog.v(TAG, "Earliest package time is " + shortestDurationMs);
+
return shortestDurationMs;
}
@@ -409,6 +453,8 @@
static class ObserverInternal {
public final String mName;
public final ArrayMap<String, MonitoredPackage> mPackages;
+ @Nullable
+ public PackageHealthObserver mRegisteredObserver;
ObserverInternal(String name, List<MonitoredPackage> packages) {
mName = name;
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index faca750..ea6d435 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -141,6 +141,7 @@
private boolean mLowPowerMode;
private int mHapticFeedbackIntensity;
private int mNotificationIntensity;
+ private int mRingIntensity;
native static boolean vibratorExists();
native static void vibratorInit();
@@ -428,6 +429,10 @@
Settings.System.getUriFor(Settings.System.NOTIFICATION_VIBRATION_INTENSITY),
true, mSettingObserver, UserHandle.USER_ALL);
+ mContext.getContentResolver().registerContentObserver(
+ Settings.System.getUriFor(Settings.System.RING_VIBRATION_INTENSITY),
+ true, mSettingObserver, UserHandle.USER_ALL);
+
mContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -748,7 +753,9 @@
}
private int getCurrentIntensityLocked(Vibration vib) {
- if (vib.isNotification() || vib.isRingtone()){
+ if (vib.isRingtone()) {
+ return mRingIntensity;
+ } else if (vib.isNotification()) {
return mNotificationIntensity;
} else if (vib.isHapticFeedback()) {
return mHapticFeedbackIntensity;
@@ -770,7 +777,9 @@
}
final int defaultIntensity;
- if (vib.isNotification() || vib.isRingtone()) {
+ if (vib.isRingtone()) {
+ defaultIntensity = mVibrator.getDefaultRingVibrationIntensity();
+ } else if (vib.isNotification()) {
defaultIntensity = mVibrator.getDefaultNotificationVibrationIntensity();
} else if (vib.isHapticFeedback()) {
defaultIntensity = mVibrator.getDefaultHapticFeedbackIntensity();
@@ -933,6 +942,9 @@
mNotificationIntensity = Settings.System.getIntForUser(mContext.getContentResolver(),
Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
mVibrator.getDefaultNotificationVibrationIntensity(), UserHandle.USER_CURRENT);
+ mRingIntensity = Settings.System.getIntForUser(mContext.getContentResolver(),
+ Settings.System.RING_VIBRATION_INTENSITY,
+ mVibrator.getDefaultRingVibrationIntensity(), UserHandle.USER_CURRENT);
}
@Override
@@ -1281,6 +1293,7 @@
pw.println(" mLowPowerMode=" + mLowPowerMode);
pw.println(" mHapticFeedbackIntensity=" + mHapticFeedbackIntensity);
pw.println(" mNotificationIntensity=" + mNotificationIntensity);
+ pw.println(" mRingIntensity=" + mRingIntensity);
pw.println("");
pw.println(" Previous vibrations:");
for (VibrationInfo info : mPreviousVibrations) {
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index ed39d83..8ca4193 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1654,6 +1654,10 @@
}
}
+ if ((flags & Context.BIND_RESTRICT_ASSOCIATIONS) != 0) {
+ mAm.requireAllowedAssociationsLocked(s.appInfo.packageName);
+ }
+
mAm.startAssociationLocked(callerApp.uid, callerApp.processName,
callerApp.getCurProcState(), s.appInfo.uid, s.appInfo.longVersionCode,
s.instanceName, s.processName);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 4f21ee8..bbe3d95 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -50,6 +50,7 @@
import static android.os.IServiceManager.DUMP_FLAG_PROTO;
import static android.os.Process.BLUETOOTH_UID;
import static android.os.Process.FIRST_APPLICATION_UID;
+import static android.os.Process.NETWORK_STACK_UID;
import static android.os.Process.NFC_UID;
import static android.os.Process.PHONE_UID;
import static android.os.Process.PROC_CHAR;
@@ -2430,6 +2431,20 @@
}
/**
+ * Ensures that the given package name has an explicit set of allowed associations.
+ * If it does not, give it an empty set.
+ */
+ void requireAllowedAssociationsLocked(String packageName) {
+ if (mAllowedAssociations == null) {
+ mAllowedAssociations = new ArrayMap<>(
+ SystemConfig.getInstance().getAllowedAssociations());
+ }
+ if (mAllowedAssociations.get(packageName) == null) {
+ mAllowedAssociations.put(packageName, new ArraySet<>());
+ }
+ }
+
+ /**
* Returns true if the package {@code pkg1} running under user handle {@code uid1} is
* allowed association with the package {@code pkg2} running under user handle {@code uid2}.
* <p> If either of the packages are running as part of the core system, then the
@@ -2437,7 +2452,8 @@
*/
boolean validateAssociationAllowedLocked(String pkg1, int uid1, String pkg2, int uid2) {
if (mAllowedAssociations == null) {
- mAllowedAssociations = SystemConfig.getInstance().getAllowedAssociations();
+ mAllowedAssociations = new ArrayMap<>(
+ SystemConfig.getInstance().getAllowedAssociations());
}
// Interactions with the system uid are always allowed, since that is the core system
// that everyone needs to be able to interact with. Also allow reflexive associations
@@ -14343,6 +14359,7 @@
case BLUETOOTH_UID:
case NFC_UID:
case SE_UID:
+ case NETWORK_STACK_UID:
isCallerSystem = true;
break;
default:
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 33af9f6..fc1e326 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -132,10 +132,13 @@
Settings.Secure.getUriFor(Settings.Secure.FACE_UNLOCK_KEYGUARD_ENABLED);
private final Uri FACE_UNLOCK_APP_ENABLED =
Settings.Secure.getUriFor(Settings.Secure.FACE_UNLOCK_APP_ENABLED);
+ private final Uri FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION =
+ Settings.Secure.getUriFor(Settings.Secure.FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION);
private final ContentResolver mContentResolver;
private boolean mFaceEnabledOnKeyguard;
private boolean mFaceEnabledForApps;
+ private boolean mFaceAlwaysRequireConfirmation;
/**
* Creates a content observer.
@@ -158,10 +161,15 @@
false /* notifyForDescendents */,
this /* observer */,
UserHandle.USER_CURRENT);
+ mContentResolver.registerContentObserver(FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION,
+ false /* notifyForDescendents */,
+ this /* observer */,
+ UserHandle.USER_CURRENT);
// Update the value immediately
onChange(true /* selfChange */, FACE_UNLOCK_KEYGUARD_ENABLED);
onChange(true /* selfChange */, FACE_UNLOCK_APP_ENABLED);
+ onChange(true /* selfChange */, FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION);
}
@Override
@@ -185,6 +193,13 @@
Settings.Secure.FACE_UNLOCK_APP_ENABLED,
1 /* default */,
UserHandle.USER_CURRENT) != 0;
+ } else if (FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION.equals(uri)) {
+ mFaceAlwaysRequireConfirmation =
+ Settings.Secure.getIntForUser(
+ mContentResolver,
+ Settings.Secure.FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION,
+ 0 /* default */,
+ UserHandle.USER_CURRENT) != 0;
}
}
@@ -195,6 +210,10 @@
boolean getFaceEnabledForApps() {
return mFaceEnabledForApps;
}
+
+ boolean getFaceAlwaysRequireConfirmation() {
+ return mFaceAlwaysRequireConfirmation;
+ }
}
private final class EnabledOnKeyguardCallback implements IBinder.DeathRecipient {
@@ -731,7 +750,7 @@
IBiometricServiceReceiver receiver, String opPackageName, Bundle bundle,
int callingUid, int callingPid, int callingUserId, int modality) {
try {
- final boolean requireConfirmation = bundle.getBoolean(
+ boolean requireConfirmation = bundle.getBoolean(
BiometricPrompt.KEY_REQUIRE_CONFIRMATION, true /* default */);
// Generate random cookies to pass to the services that should prepare to start
@@ -757,6 +776,9 @@
Slog.w(TAG, "Iris unsupported");
}
if ((modality & TYPE_FACE) != 0) {
+ // Check if the user has forced confirmation to be required in Settings.
+ requireConfirmation = requireConfirmation
+ || mSettingObserver.getFaceAlwaysRequireConfirmation();
mFaceService.prepareForAuthentication(requireConfirmation,
token, sessionId, userId, mInternalReceiver, opPackageName,
cookie, callingUid, callingPid, callingUserId);
diff --git a/services/core/java/com/android/server/connectivity/ConnectivityConstants.java b/services/core/java/com/android/server/connectivity/ConnectivityConstants.java
index 24865bc..6fa98b8 100644
--- a/services/core/java/com/android/server/connectivity/ConnectivityConstants.java
+++ b/services/core/java/com/android/server/connectivity/ConnectivityConstants.java
@@ -21,22 +21,6 @@
* @hide
*/
public class ConnectivityConstants {
- // IPC constants
- public static final String ACTION_NETWORK_CONDITIONS_MEASURED =
- "android.net.conn.NETWORK_CONDITIONS_MEASURED";
- public static final String EXTRA_CONNECTIVITY_TYPE = "extra_connectivity_type";
- public static final String EXTRA_NETWORK_TYPE = "extra_network_type";
- public static final String EXTRA_RESPONSE_RECEIVED = "extra_response_received";
- public static final String EXTRA_IS_CAPTIVE_PORTAL = "extra_is_captive_portal";
- public static final String EXTRA_CELL_ID = "extra_cellid";
- public static final String EXTRA_SSID = "extra_ssid";
- public static final String EXTRA_BSSID = "extra_bssid";
- /** real time since boot */
- public static final String EXTRA_REQUEST_TIMESTAMP_MS = "extra_request_timestamp_ms";
- public static final String EXTRA_RESPONSE_TIMESTAMP_MS = "extra_response_timestamp_ms";
-
- public static final String PERMISSION_ACCESS_NETWORK_CONDITIONS =
- "android.permission.ACCESS_NETWORK_CONDITIONS";
// Penalty applied to scores of Networks that have not been validated.
public static final int UNVALIDATED_SCORE_PENALTY = 40;
diff --git a/services/core/java/com/android/server/connectivity/DnsManager.java b/services/core/java/com/android/server/connectivity/DnsManager.java
index b8f057d..d8bb635 100644
--- a/services/core/java/com/android/server/connectivity/DnsManager.java
+++ b/services/core/java/com/android/server/connectivity/DnsManager.java
@@ -18,10 +18,9 @@
import static android.net.ConnectivityManager.PRIVATE_DNS_DEFAULT_MODE_FALLBACK;
import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF;
-import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
-import static android.provider.Settings.Global.DNS_RESOLVER_MIN_SAMPLES;
import static android.provider.Settings.Global.DNS_RESOLVER_MAX_SAMPLES;
+import static android.provider.Settings.Global.DNS_RESOLVER_MIN_SAMPLES;
import static android.provider.Settings.Global.DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS;
import static android.provider.Settings.Global.DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT;
import static android.provider.Settings.Global.PRIVATE_DNS_DEFAULT_MODE;
@@ -35,6 +34,7 @@
import android.net.Network;
import android.net.NetworkUtils;
import android.net.Uri;
+import android.net.shared.PrivateDnsConfig;
import android.os.Binder;
import android.os.INetworkManagementService;
import android.os.UserHandle;
@@ -43,10 +43,7 @@
import android.util.Pair;
import android.util.Slog;
-import com.android.server.connectivity.MockableSystemProperties;
-
import java.net.InetAddress;
-import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
@@ -54,10 +51,8 @@
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
-import java.util.Objects;
-import java.util.stream.Collectors;
import java.util.Set;
-import java.util.StringJoiner;
+import java.util.stream.Collectors;
/**
@@ -123,43 +118,6 @@
private static final int DNS_RESOLVER_DEFAULT_MIN_SAMPLES = 8;
private static final int DNS_RESOLVER_DEFAULT_MAX_SAMPLES = 64;
- public static class PrivateDnsConfig {
- public final boolean useTls;
- public final String hostname;
- public final InetAddress[] ips;
-
- public PrivateDnsConfig() {
- this(false);
- }
-
- public PrivateDnsConfig(boolean useTls) {
- this.useTls = useTls;
- this.hostname = "";
- this.ips = new InetAddress[0];
- }
-
- public PrivateDnsConfig(String hostname, InetAddress[] ips) {
- this.useTls = !TextUtils.isEmpty(hostname);
- this.hostname = useTls ? hostname : "";
- this.ips = (ips != null) ? ips : new InetAddress[0];
- }
-
- public PrivateDnsConfig(PrivateDnsConfig cfg) {
- useTls = cfg.useTls;
- hostname = cfg.hostname;
- ips = cfg.ips;
- }
-
- public boolean inStrictMode() {
- return useTls && !TextUtils.isEmpty(hostname);
- }
-
- public String toString() {
- return PrivateDnsConfig.class.getSimpleName() +
- "{" + useTls + ":" + hostname + "/" + Arrays.toString(ips) + "}";
- }
- }
-
public static PrivateDnsConfig getPrivateDnsConfig(ContentResolver cr) {
final String mode = getPrivateDnsMode(cr);
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 262184b..54c89aa 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -16,9 +16,8 @@
package com.android.server.connectivity;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
-
import android.content.Context;
+import android.net.INetworkMonitor;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
@@ -29,7 +28,6 @@
import android.os.Handler;
import android.os.INetworkManagementService;
import android.os.Messenger;
-import android.os.RemoteException;
import android.os.SystemClock;
import android.util.Log;
import android.util.SparseArray;
@@ -37,11 +35,8 @@
import com.android.internal.util.AsyncChannel;
import com.android.internal.util.WakeupMessage;
import com.android.server.ConnectivityService;
-import com.android.server.connectivity.NetworkMonitor;
import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Comparator;
import java.util.Objects;
import java.util.SortedSet;
import java.util.TreeSet;
@@ -126,7 +121,6 @@
public LinkProperties linkProperties;
// This should only be modified via ConnectivityService.updateCapabilities().
public NetworkCapabilities networkCapabilities;
- public final NetworkMonitor networkMonitor;
public final NetworkMisc networkMisc;
// Indicates if netd has been told to create this Network. From this point on the appropriate
// routing rules are setup and routes are added so packets can begin flowing over the Network.
@@ -239,6 +233,9 @@
// Used by ConnectivityService to keep track of 464xlat.
public Nat464Xlat clatd;
+ // Set after asynchronous creation of the NetworkMonitor.
+ private volatile INetworkMonitor mNetworkMonitor;
+
private static final String TAG = ConnectivityService.class.getSimpleName();
private static final boolean VDBG = false;
private final ConnectivityService mConnService;
@@ -247,7 +244,7 @@
public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, Network net, NetworkInfo info,
LinkProperties lp, NetworkCapabilities nc, int score, Context context, Handler handler,
- NetworkMisc misc, NetworkRequest defaultRequest, ConnectivityService connService) {
+ NetworkMisc misc, ConnectivityService connService) {
this.messenger = messenger;
asyncChannel = ac;
network = net;
@@ -258,10 +255,16 @@
mConnService = connService;
mContext = context;
mHandler = handler;
- networkMonitor = mConnService.createNetworkMonitor(context, handler, this, defaultRequest);
networkMisc = misc;
}
+ /**
+ * Inform NetworkAgentInfo that a new NetworkMonitor was created.
+ */
+ public void onNetworkMonitorCreated(INetworkMonitor networkMonitor) {
+ mNetworkMonitor = networkMonitor;
+ }
+
public ConnectivityService connService() {
return mConnService;
}
@@ -278,6 +281,15 @@
return network;
}
+ /**
+ * Get the INetworkMonitor in this NetworkAgentInfo.
+ *
+ * <p>This will be null before {@link #onNetworkMonitorCreated(INetworkMonitor)} is called.
+ */
+ public INetworkMonitor networkMonitor() {
+ return mNetworkMonitor;
+ }
+
// Functions for manipulating the requests satisfied by this network.
//
// These functions must only called on ConnectivityService's main thread.
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
index 3264790..d4b8eb2 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
@@ -16,7 +16,15 @@
package com.android.server.inputmethod;
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
import android.content.ComponentName;
+import android.view.inputmethod.InputMethodInfo;
+
+import com.android.server.LocalServices;
+
+import java.util.Collections;
+import java.util.List;
/**
* Input method manager local system service interface.
@@ -39,9 +47,25 @@
public abstract void startVrInputMethodNoCheck(ComponentName componentName);
/**
+ * Returns the list of installed input methods for the specified user.
+ *
+ * @param userId The user ID to be queried.
+ * @return A list of {@link InputMethodInfo}. VR-only IMEs are already excluded.
+ */
+ public abstract List<InputMethodInfo> getInputMethodListAsUser(@UserIdInt int userId);
+
+ /**
+ * Returns the list of installed input methods that are enabled for the specified user.
+ *
+ * @param userId The user ID to be queried.
+ * @return A list of {@link InputMethodInfo} that are enabled for {@code userId}.
+ */
+ public abstract List<InputMethodInfo> getEnabledInputMethodListAsUser(@UserIdInt int userId);
+
+ /**
* Fake implementation of {@link InputMethodManagerInternal}. All the methods do nothing.
*/
- public static final InputMethodManagerInternal NOP =
+ private static final InputMethodManagerInternal NOP =
new InputMethodManagerInternal() {
@Override
public void setInteractive(boolean interactive) {
@@ -54,5 +78,25 @@
@Override
public void startVrInputMethodNoCheck(ComponentName componentName) {
}
+
+ @Override
+ public List<InputMethodInfo> getInputMethodListAsUser(int userId) {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public List<InputMethodInfo> getEnabledInputMethodListAsUser(int userId) {
+ return Collections.emptyList();
+ }
};
+
+ /**
+ * @return Global instance if exists. Otherwise, a dummy no-op instance.
+ */
+ @NonNull
+ public static InputMethodManagerInternal get() {
+ final InputMethodManagerInternal instance =
+ LocalServices.getService(InputMethodManagerInternal.class);
+ return instance != null ? instance : NOP;
+ }
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 840c6e4..5fa3f52 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -20,6 +20,7 @@
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
+import static android.view.inputmethod.InputMethodSystemProperty.PER_PROFILE_IME_ENABLED;
import static java.lang.annotation.RetentionPolicy.SOURCE;
@@ -1603,7 +1604,7 @@
// 1) it comes from the system process
// 2) the calling process' user id is identical to the current user id IMMS thinks.
@GuardedBy("mMethodMap")
- private boolean calledFromValidUserLocked() {
+ private boolean calledFromValidUserLocked(boolean allowCrossProfileAccess) {
final int uid = Binder.getCallingUid();
final int userId = UserHandle.getUserId(uid);
if (DEBUG) {
@@ -1613,7 +1614,13 @@
+ mSettings.getCurrentUserId() + ", calling pid = " + Binder.getCallingPid()
+ InputMethodUtils.getApiCallStack());
}
- if (uid == Process.SYSTEM_UID || mSettings.isCurrentProfile(userId)) {
+ if (uid == Process.SYSTEM_UID) {
+ return true;
+ }
+ if (userId == mSettings.getCurrentUserId()) {
+ return true;
+ }
+ if (allowCrossProfileAccess && mSettings.isCurrentProfile(userId)) {
return true;
}
@@ -2518,7 +2525,7 @@
@Override
public void registerSuggestionSpansForNotification(SuggestionSpan[] spans) {
synchronized (mMethodMap) {
- if (!calledFromValidUserLocked()) {
+ if (!calledFromValidUserLocked(!PER_PROFILE_IME_ENABLED)) {
return;
}
final InputMethodInfo currentImi = mMethodMap.get(mCurMethodId);
@@ -2534,7 +2541,7 @@
@Override
public boolean notifySuggestionPicked(SuggestionSpan span, String originalString, int index) {
synchronized (mMethodMap) {
- if (!calledFromValidUserLocked()) {
+ if (!calledFromValidUserLocked(!PER_PROFILE_IME_ENABLED)) {
return false;
}
final InputMethodInfo targetImi = mSecureSuggestionSpans.get(span);
@@ -2701,7 +2708,7 @@
ResultReceiver resultReceiver) {
int uid = Binder.getCallingUid();
synchronized (mMethodMap) {
- if (!calledFromValidUserLocked()) {
+ if (!calledFromValidUserLocked(!PER_PROFILE_IME_ENABLED)) {
return false;
}
final long ident = Binder.clearCallingIdentity();
@@ -2786,7 +2793,7 @@
ResultReceiver resultReceiver) {
int uid = Binder.getCallingUid();
synchronized (mMethodMap) {
- if (!calledFromValidUserLocked()) {
+ if (!calledFromValidUserLocked(!PER_PROFILE_IME_ENABLED)) {
return false;
}
final long ident = Binder.clearCallingIdentity();
@@ -2893,10 +2900,12 @@
@SoftInputModeFlags int softInputMode, int windowFlags, EditorInfo attribute,
IInputContext inputContext, @MissingMethodFlags int missingMethods,
int unverifiedTargetSdkVersion) {
+ final int userId = UserHandle.getUserId(Binder.getCallingUid());
InputBindResult res = null;
synchronized (mMethodMap) {
// Needs to check the validity before clearing calling identity
- final boolean calledFromValidUser = calledFromValidUserLocked();
+ // Note that cross-profile access is always allowed here to allow profile-switching.
+ final boolean calledFromValidUser = calledFromValidUserLocked(true);
final int windowDisplayId =
mWindowManagerInternal.getDisplayIdForWindow(windowToken);
final long ident = Binder.clearCallingIdentity();
@@ -2948,6 +2957,10 @@
return InputBindResult.INVALID_USER;
}
+ if (PER_PROFILE_IME_ENABLED && userId != mSettings.getCurrentUserId()) {
+ switchUserLocked(userId);
+ }
+
if (mCurFocusedWindow == windowToken) {
if (DEBUG) {
Slog.w(TAG, "Window already focused, ignoring focus gain of: " + client
@@ -3116,7 +3129,7 @@
public void showInputMethodPickerFromClient(
IInputMethodClient client, int auxiliarySubtypeMode) {
synchronized (mMethodMap) {
- if (!calledFromValidUserLocked()) {
+ if (!calledFromValidUserLocked(!PER_PROFILE_IME_ENABLED)) {
return;
}
if(!canShowInputMethodPickerLocked(client)) {
@@ -3187,7 +3200,7 @@
IInputMethodClient client, String inputMethodId) {
synchronized (mMethodMap) {
// TODO(yukawa): Should we verify the display ID?
- if (!calledFromValidUserLocked()) {
+ if (!calledFromValidUserLocked(!PER_PROFILE_IME_ENABLED)) {
return;
}
executeOrSendMessage(mCurMethod, mCaller.obtainMessageO(
@@ -3302,7 +3315,7 @@
@Override
public InputMethodSubtype getLastInputMethodSubtype() {
synchronized (mMethodMap) {
- if (!calledFromValidUserLocked()) {
+ if (!calledFromValidUserLocked(!PER_PROFILE_IME_ENABLED)) {
return null;
}
final Pair<String, String> lastIme = mSettings.getLastInputMethodAndSubtypeLocked();
@@ -3340,7 +3353,7 @@
}
}
synchronized (mMethodMap) {
- if (!calledFromValidUserLocked()) {
+ if (!calledFromValidUserLocked(!PER_PROFILE_IME_ENABLED)) {
return;
}
if (!mSystemReady) {
@@ -4186,7 +4199,7 @@
public InputMethodSubtype getCurrentInputMethodSubtype() {
synchronized (mMethodMap) {
// TODO: Make this work even for non-current users?
- if (!calledFromValidUserLocked()) {
+ if (!calledFromValidUserLocked(!PER_PROFILE_IME_ENABLED)) {
return null;
}
return getCurrentInputMethodSubtypeLocked();
@@ -4236,7 +4249,7 @@
public boolean setCurrentInputMethodSubtype(InputMethodSubtype subtype) {
synchronized (mMethodMap) {
// TODO: Make this work even for non-current users?
- if (!calledFromValidUserLocked()) {
+ if (!calledFromValidUserLocked(!PER_PROFILE_IME_ENABLED)) {
return false;
}
if (subtype != null && mCurMethodId != null) {
@@ -4251,6 +4264,18 @@
}
}
+ private List<InputMethodInfo> getInputMethodListAsUser(@UserIdInt int userId) {
+ synchronized (mMethodMap) {
+ return getInputMethodListLocked(false, userId);
+ }
+ }
+
+ private List<InputMethodInfo> getEnabledInputMethodListAsUser(@UserIdInt int userId) {
+ synchronized (mMethodMap) {
+ return getEnabledInputMethodListLocked(userId);
+ }
+ }
+
private static final class LocalServiceImpl extends InputMethodManagerInternal {
@NonNull
private final InputMethodManagerService mService;
@@ -4276,6 +4301,16 @@
public void startVrInputMethodNoCheck(@Nullable ComponentName componentName) {
mService.mHandler.obtainMessage(MSG_START_VR_INPUT, componentName).sendToTarget();
}
+
+ @Override
+ public List<InputMethodInfo> getInputMethodListAsUser(int userId) {
+ return mService.getInputMethodListAsUser(userId);
+ }
+
+ @Override
+ public List<InputMethodInfo> getEnabledInputMethodListAsUser(int userId) {
+ return mService.getEnabledInputMethodListAsUser(userId);
+ }
}
@BinderThread
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
index 2f76871..88d1a9c 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
@@ -40,6 +40,7 @@
import android.util.Slog;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodSubtype;
+import android.view.inputmethod.InputMethodSystemProperty;
import android.view.textservice.SpellCheckerInfo;
import com.android.internal.annotations.GuardedBy;
@@ -1295,7 +1296,8 @@
* Converts a user ID, which can be a pseudo user ID such as {@link UserHandle#USER_ALL} to a
* list of real user IDs.
*
- * <p>Currently this method also converts profile user ID to profile parent user ID.</p>
+ * <p>This method also converts profile user ID to profile parent user ID unless
+ * {@link InputMethodSystemProperty#PER_PROFILE_IME_ENABLED} is {@code true}.</p>
*
* @param userIdToBeResolved A user ID. Two pseudo user ID {@link UserHandle#USER_CURRENT} and
* {@link UserHandle#USER_ALL} are also supported
@@ -1311,6 +1313,9 @@
LocalServices.getService(UserManagerInternal.class);
if (userIdToBeResolved == UserHandle.USER_ALL) {
+ if (InputMethodSystemProperty.PER_PROFILE_IME_ENABLED) {
+ return userManagerInternal.getUserIds();
+ }
final IntArray result = new IntArray();
for (int userId : userManagerInternal.getUserIds()) {
final int parentUserId = userManagerInternal.getProfileParentId(userId);
@@ -1341,6 +1346,8 @@
}
return new int[]{};
}
- return new int[]{userManagerInternal.getProfileParentId(sourceUserId)};
+ final int resolvedUserId = InputMethodSystemProperty.PER_PROFILE_IME_ENABLED
+ ? sourceUserId : userManagerInternal.getProfileParentId(sourceUserId);
+ return new int[]{resolvedUserId};
}
}
diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
index 98ed3ea..f304ceb 100644
--- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
@@ -163,6 +163,18 @@
public void startVrInputMethodNoCheck(ComponentName componentName) {
reportNotSupported();
}
+
+ @Override
+ public List<InputMethodInfo> getInputMethodListAsUser(
+ @UserIdInt int userId) {
+ return userIdToInputMethodInfoMapper.getAsList(userId);
+ }
+
+ @Override
+ public List<InputMethodInfo> getEnabledInputMethodListAsUser(
+ @UserIdInt int userId) {
+ return userIdToInputMethodInfoMapper.getAsList(userId);
+ }
});
}
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 8ecceb9..d611a17 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -19,7 +19,6 @@
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.ParceledListSlice;
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.AudioManagerInternal;
@@ -27,14 +26,15 @@
import android.media.MediaMetadata;
import android.media.Rating;
import android.media.VolumeProvider;
+import android.media.session.ControllerCallbackLink;
import android.media.session.ISession;
-import android.media.session.ISessionCallback;
import android.media.session.ISessionController;
-import android.media.session.ISessionControllerCallback;
import android.media.session.MediaController;
import android.media.session.MediaController.PlaybackInfo;
import android.media.session.MediaSession;
+import android.media.session.MediaSession.QueueItem;
import android.media.session.PlaybackState;
+import android.media.session.SessionCallbackLink;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
@@ -44,7 +44,6 @@
import android.os.Looper;
import android.os.Message;
import android.os.Process;
-import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.SystemClock;
import android.util.Log;
@@ -55,6 +54,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.List;
/**
* This is the system implementation of a Session. Apps will interact with the
@@ -84,7 +84,7 @@
private final Context mContext;
private final Object mLock = new Object();
- private final ArrayList<ISessionControllerCallbackHolder> mControllerCallbackHolders =
+ private final ArrayList<ControllerCallbackLinkHolder> mControllerCallbackHolders =
new ArrayList<>();
private long mFlags;
@@ -97,7 +97,7 @@
// may result in throwing an exception.
private MediaMetadata mMetadata;
private PlaybackState mPlaybackState;
- private ParceledListSlice mQueue;
+ private List<QueueItem> mQueue;
private CharSequence mQueueTitle;
private int mRatingType;
// End TransportPerformer fields
@@ -120,7 +120,7 @@
private String mMetadataDescription;
public MediaSessionRecord(int ownerPid, int ownerUid, int userId, String ownerPackageName,
- ISessionCallback cb, String tag, MediaSessionService service, Looper handlerLooper) {
+ SessionCallbackLink cb, String tag, MediaSessionService service, Looper handlerLooper) {
mOwnerPid = ownerPid;
mOwnerUid = ownerUid;
mUserId = userId;
@@ -249,7 +249,7 @@
* @param useSuggested True to use adjustSuggestedStreamVolume instead of
*/
public void adjustVolume(String packageName, String opPackageName, int pid, int uid,
- ISessionControllerCallback caller, boolean asSystemService, int direction, int flags,
+ ControllerCallbackLink caller, boolean asSystemService, int direction, int flags,
boolean useSuggested) {
int previousFlagPlaySound = flags & AudioManager.FLAG_PLAY_SOUND;
if (isPlaybackActive() || hasFlag(MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY)) {
@@ -291,7 +291,7 @@
}
private void setVolumeTo(String packageName, String opPackageName, int pid, int uid,
- ISessionControllerCallback caller, int value, int flags) {
+ ControllerCallbackLink caller, int value, int flags) {
if (mVolumeType == PlaybackInfo.PLAYBACK_TYPE_LOCAL) {
int stream = AudioAttributes.toLegacyStreamType(mAudioAttrs);
final int volumeValue = value;
@@ -440,7 +440,7 @@
}
}
- public ISessionCallback getCallback() {
+ public SessionCallbackLink getCallback() {
return mSessionCb.mCb;
}
@@ -468,7 +468,7 @@
+ ", max=" + mMaxVolume + ", current=" + mCurrentVolume);
pw.println(indent + "metadata: " + mMetadataDescription);
pw.println(indent + "queueTitle=" + mQueueTitle + ", size="
- + (mQueue == null ? 0 : mQueue.getList().size()));
+ + (mQueue == null ? 0 : mQueue.size()));
}
@Override
@@ -518,7 +518,7 @@
}
private void logCallbackException(
- String msg, ISessionControllerCallbackHolder holder, Exception e) {
+ String msg, ControllerCallbackLinkHolder holder, Exception e) {
Log.v(TAG, msg + ", this=" + this + ", callback package=" + holder.mPackageName
+ ", exception=" + e);
}
@@ -529,16 +529,18 @@
return;
}
for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) {
- ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i);
+ ControllerCallbackLinkHolder holder = mControllerCallbackHolders.get(i);
try {
- holder.mCallback.onPlaybackStateChanged(mPlaybackState);
- } catch (DeadObjectException e) {
- mControllerCallbackHolders.remove(i);
- logCallbackException("Removed dead callback in pushPlaybackStateUpdate",
- holder, e);
- } catch (RemoteException e) {
- logCallbackException("unexpected exception in pushPlaybackStateUpdate",
- holder, e);
+ holder.mCallback.notifyPlaybackStateChanged(mPlaybackState);
+ } catch (RuntimeException e) {
+ if (e.getCause() instanceof DeadObjectException) {
+ mControllerCallbackHolders.remove(i);
+ logCallbackException("Removing dead callback in pushPlaybackStateUpdate",
+ holder, e);
+ } else {
+ logCallbackException("unexpected exception in pushPlaybackStateUpdate",
+ holder, e);
+ }
}
}
}
@@ -550,14 +552,18 @@
return;
}
for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) {
- ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i);
+ ControllerCallbackLinkHolder holder = mControllerCallbackHolders.get(i);
try {
- holder.mCallback.onMetadataChanged(mMetadata);
- } catch (DeadObjectException e) {
- logCallbackException("Removing dead callback in pushMetadataUpdate", holder, e);
- mControllerCallbackHolders.remove(i);
- } catch (RemoteException e) {
- logCallbackException("unexpected exception in pushMetadataUpdate", holder, e);
+ holder.mCallback.notifyMetadataChanged(mMetadata);
+ } catch (RuntimeException e) {
+ if (e.getCause() instanceof DeadObjectException) {
+ mControllerCallbackHolders.remove(i);
+ logCallbackException("Removing dead callback in pushMetadataUpdate",
+ holder, e);
+ } else {
+ logCallbackException("unexpected exception in pushMetadataUpdate",
+ holder, e);
+ }
}
}
}
@@ -569,14 +575,17 @@
return;
}
for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) {
- ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i);
+ ControllerCallbackLinkHolder holder = mControllerCallbackHolders.get(i);
try {
- holder.mCallback.onQueueChanged(mQueue);
- } catch (DeadObjectException e) {
- mControllerCallbackHolders.remove(i);
- logCallbackException("Removed dead callback in pushQueueUpdate", holder, e);
- } catch (RemoteException e) {
- logCallbackException("unexpected exception in pushQueueUpdate", holder, e);
+ holder.mCallback.notifyQueueChanged(mQueue);
+ } catch (RuntimeException e) {
+ if (e.getCause() instanceof DeadObjectException) {
+ mControllerCallbackHolders.remove(i);
+ logCallbackException("Removing dead callback in pushQueueUpdate",
+ holder, e);
+ } else {
+ logCallbackException("unexpected exception in pushQueueUpdate", holder, e);
+ }
}
}
}
@@ -588,16 +597,18 @@
return;
}
for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) {
- ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i);
+ ControllerCallbackLinkHolder holder = mControllerCallbackHolders.get(i);
try {
- holder.mCallback.onQueueTitleChanged(mQueueTitle);
- } catch (DeadObjectException e) {
- mControllerCallbackHolders.remove(i);
- logCallbackException("Removed dead callback in pushQueueTitleUpdate",
- holder, e);
- } catch (RemoteException e) {
- logCallbackException("unexpected exception in pushQueueTitleUpdate",
- holder, e);
+ holder.mCallback.notifyQueueTitleChanged(mQueueTitle);
+ } catch (RuntimeException e) {
+ if (e.getCause() instanceof DeadObjectException) {
+ mControllerCallbackHolders.remove(i);
+ logCallbackException("Removing dead callback in pushQueueTitleUpdate",
+ holder, e);
+ } else {
+ logCallbackException("unexpected exception in pushQueueTitleUpdate",
+ holder, e);
+ }
}
}
}
@@ -609,14 +620,17 @@
return;
}
for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) {
- ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i);
+ ControllerCallbackLinkHolder holder = mControllerCallbackHolders.get(i);
try {
- holder.mCallback.onExtrasChanged(mExtras);
- } catch (DeadObjectException e) {
- mControllerCallbackHolders.remove(i);
- logCallbackException("Removed dead callback in pushExtrasUpdate", holder, e);
- } catch (RemoteException e) {
- logCallbackException("unexpected exception in pushExtrasUpdate", holder, e);
+ holder.mCallback.notifyExtrasChanged(mExtras);
+ } catch (RuntimeException e) {
+ if (e.getCause() instanceof DeadObjectException) {
+ mControllerCallbackHolders.remove(i);
+ logCallbackException("Removing dead callback in pushExtrasUpdate",
+ holder, e);
+ } else {
+ logCallbackException("unexpected exception in pushExtrasUpdate", holder, e);
+ }
}
}
}
@@ -629,14 +643,17 @@
}
PlaybackInfo info = mController.getVolumeAttributes();
for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) {
- ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i);
+ ControllerCallbackLinkHolder holder = mControllerCallbackHolders.get(i);
try {
- holder.mCallback.onVolumeInfoChanged(info);
- } catch (DeadObjectException e) {
- mControllerCallbackHolders.remove(i);
- logCallbackException("Removing dead callback in pushVolumeUpdate", holder, e);
- } catch (RemoteException e) {
- logCallbackException("Unexpected exception in pushVolumeUpdate", holder, e);
+ holder.mCallback.notifyVolumeInfoChanged(info);
+ } catch (RuntimeException e) {
+ if (e.getCause() instanceof DeadObjectException) {
+ mControllerCallbackHolders.remove(i);
+ logCallbackException("Removing dead callback in pushVolumeUpdate",
+ holder, e);
+ } else {
+ logCallbackException("unexpected exception in pushVolumeUpdate", holder, e);
+ }
}
}
}
@@ -648,14 +665,16 @@
return;
}
for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) {
- ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i);
+ ControllerCallbackLinkHolder holder = mControllerCallbackHolders.get(i);
try {
- holder.mCallback.onEvent(event, data);
- } catch (DeadObjectException e) {
- mControllerCallbackHolders.remove(i);
- logCallbackException("Removing dead callback in pushEvent", holder, e);
- } catch (RemoteException e) {
- logCallbackException("unexpected exception in pushEvent", holder, e);
+ holder.mCallback.notifyEvent(event, data);
+ } catch (RuntimeException e) {
+ if (e.getCause() instanceof DeadObjectException) {
+ mControllerCallbackHolders.remove(i);
+ logCallbackException("Removing dead callback in pushEvent", holder, e);
+ } else {
+ logCallbackException("unexpected exception in pushEvent", holder, e);
+ }
}
}
}
@@ -669,14 +688,18 @@
return;
}
for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) {
- ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i);
+ ControllerCallbackLinkHolder holder = mControllerCallbackHolders.get(i);
try {
- holder.mCallback.onSessionDestroyed();
- } catch (DeadObjectException e) {
- logCallbackException("Removing dead callback in pushEvent", holder, e);
- mControllerCallbackHolders.remove(i);
- } catch (RemoteException e) {
- logCallbackException("unexpected exception in pushEvent", holder, e);
+ holder.mCallback.notifySessionDestroyed();
+ } catch (RuntimeException e) {
+ if (e.getCause() instanceof DeadObjectException) {
+ mControllerCallbackHolders.remove(i);
+ logCallbackException("Removing dead callback in pushSessionDestroyed",
+ holder, e);
+ } else {
+ logCallbackException("unexpected exception in pushSessionDestroyed",
+ holder, e);
+ }
}
}
// After notifying clear all listeners
@@ -716,10 +739,10 @@
return result == null ? state : result;
}
- private int getControllerHolderIndexForCb(ISessionControllerCallback cb) {
- IBinder binder = cb.asBinder();
+ private int getControllerHolderIndexForCb(ControllerCallbackLink cb) {
+ IBinder binder = cb.getBinder();
for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) {
- if (binder.equals(mControllerCallbackHolders.get(i).mCallback.asBinder())) {
+ if (binder.equals(mControllerCallbackHolders.get(i).mCallback.getBinder())) {
return i;
}
}
@@ -843,7 +866,7 @@
}
@Override
- public void setQueue(ParceledListSlice queue) {
+ public void setQueue(List<QueueItem> queue) {
synchronized (mLock) {
mQueue = queue;
}
@@ -920,9 +943,9 @@
}
class SessionCb {
- private final ISessionCallback mCb;
+ private final SessionCallbackLink mCb;
- public SessionCb(ISessionCallback cb) {
+ SessionCb(SessionCallbackLink cb) {
mCb = cb;
}
@@ -930,224 +953,224 @@
boolean asSystemService, KeyEvent keyEvent, int sequenceId, ResultReceiver cb) {
try {
if (asSystemService) {
- mCb.onMediaButton(mContext.getPackageName(), Process.myPid(),
+ mCb.notifyMediaButton(mContext.getPackageName(), Process.myPid(),
Process.SYSTEM_UID, createMediaButtonIntent(keyEvent), sequenceId, cb);
} else {
- mCb.onMediaButton(packageName, pid, uid,
+ mCb.notifyMediaButton(packageName, pid, uid,
createMediaButtonIntent(keyEvent), sequenceId, cb);
}
return true;
- } catch (RemoteException e) {
+ } catch (RuntimeException e) {
Slog.e(TAG, "Remote failure in sendMediaRequest.", e);
}
return false;
}
public boolean sendMediaButton(String packageName, int pid, int uid,
- ISessionControllerCallback caller, boolean asSystemService,
+ ControllerCallbackLink caller, boolean asSystemService,
KeyEvent keyEvent) {
try {
if (asSystemService) {
- mCb.onMediaButton(mContext.getPackageName(), Process.myPid(),
+ mCb.notifyMediaButton(mContext.getPackageName(), Process.myPid(),
Process.SYSTEM_UID, createMediaButtonIntent(keyEvent), 0, null);
} else {
- mCb.onMediaButtonFromController(packageName, pid, uid, caller,
+ mCb.notifyMediaButtonFromController(packageName, pid, uid, caller,
createMediaButtonIntent(keyEvent));
}
return true;
- } catch (RemoteException e) {
+ } catch (RuntimeException e) {
Slog.e(TAG, "Remote failure in sendMediaRequest.", e);
}
return false;
}
public void sendCommand(String packageName, int pid, int uid,
- ISessionControllerCallback caller, String command, Bundle args, ResultReceiver cb) {
+ ControllerCallbackLink caller, String command, Bundle args, ResultReceiver cb) {
try {
- mCb.onCommand(packageName, pid, uid, caller, command, args, cb);
- } catch (RemoteException e) {
+ mCb.notifyCommand(packageName, pid, uid, caller, command, args, cb);
+ } catch (RuntimeException e) {
Slog.e(TAG, "Remote failure in sendCommand.", e);
}
}
public void sendCustomAction(String packageName, int pid, int uid,
- ISessionControllerCallback caller, String action,
+ ControllerCallbackLink caller, String action,
Bundle args) {
try {
- mCb.onCustomAction(packageName, pid, uid, caller, action, args);
- } catch (RemoteException e) {
+ mCb.notifyCustomAction(packageName, pid, uid, caller, action, args);
+ } catch (RuntimeException e) {
Slog.e(TAG, "Remote failure in sendCustomAction.", e);
}
}
public void prepare(String packageName, int pid, int uid,
- ISessionControllerCallback caller) {
+ ControllerCallbackLink caller) {
try {
- mCb.onPrepare(packageName, pid, uid, caller);
- } catch (RemoteException e) {
+ mCb.notifyPrepare(packageName, pid, uid, caller);
+ } catch (RuntimeException e) {
Slog.e(TAG, "Remote failure in prepare.", e);
}
}
public void prepareFromMediaId(String packageName, int pid, int uid,
- ISessionControllerCallback caller, String mediaId, Bundle extras) {
+ ControllerCallbackLink caller, String mediaId, Bundle extras) {
try {
- mCb.onPrepareFromMediaId(packageName, pid, uid, caller, mediaId, extras);
- } catch (RemoteException e) {
+ mCb.notifyPrepareFromMediaId(packageName, pid, uid, caller, mediaId, extras);
+ } catch (RuntimeException e) {
Slog.e(TAG, "Remote failure in prepareFromMediaId.", e);
}
}
public void prepareFromSearch(String packageName, int pid, int uid,
- ISessionControllerCallback caller, String query, Bundle extras) {
+ ControllerCallbackLink caller, String query, Bundle extras) {
try {
- mCb.onPrepareFromSearch(packageName, pid, uid, caller, query, extras);
- } catch (RemoteException e) {
+ mCb.notifyPrepareFromSearch(packageName, pid, uid, caller, query, extras);
+ } catch (RuntimeException e) {
Slog.e(TAG, "Remote failure in prepareFromSearch.", e);
}
}
public void prepareFromUri(String packageName, int pid, int uid,
- ISessionControllerCallback caller, Uri uri, Bundle extras) {
+ ControllerCallbackLink caller, Uri uri, Bundle extras) {
try {
- mCb.onPrepareFromUri(packageName, pid, uid, caller, uri, extras);
- } catch (RemoteException e) {
+ mCb.notifyPrepareFromUri(packageName, pid, uid, caller, uri, extras);
+ } catch (RuntimeException e) {
Slog.e(TAG, "Remote failure in prepareFromUri.", e);
}
}
- public void play(String packageName, int pid, int uid, ISessionControllerCallback caller) {
+ public void play(String packageName, int pid, int uid, ControllerCallbackLink caller) {
try {
- mCb.onPlay(packageName, pid, uid, caller);
- } catch (RemoteException e) {
+ mCb.notifyPlay(packageName, pid, uid, caller);
+ } catch (RuntimeException e) {
Slog.e(TAG, "Remote failure in play.", e);
}
}
public void playFromMediaId(String packageName, int pid, int uid,
- ISessionControllerCallback caller, String mediaId, Bundle extras) {
+ ControllerCallbackLink caller, String mediaId, Bundle extras) {
try {
- mCb.onPlayFromMediaId(packageName, pid, uid, caller, mediaId, extras);
- } catch (RemoteException e) {
+ mCb.notifyPlayFromMediaId(packageName, pid, uid, caller, mediaId, extras);
+ } catch (RuntimeException e) {
Slog.e(TAG, "Remote failure in playFromMediaId.", e);
}
}
public void playFromSearch(String packageName, int pid, int uid,
- ISessionControllerCallback caller, String query, Bundle extras) {
+ ControllerCallbackLink caller, String query, Bundle extras) {
try {
- mCb.onPlayFromSearch(packageName, pid, uid, caller, query, extras);
- } catch (RemoteException e) {
+ mCb.notifyPlayFromSearch(packageName, pid, uid, caller, query, extras);
+ } catch (RuntimeException e) {
Slog.e(TAG, "Remote failure in playFromSearch.", e);
}
}
public void playFromUri(String packageName, int pid, int uid,
- ISessionControllerCallback caller, Uri uri, Bundle extras) {
+ ControllerCallbackLink caller, Uri uri, Bundle extras) {
try {
- mCb.onPlayFromUri(packageName, pid, uid, caller, uri, extras);
- } catch (RemoteException e) {
+ mCb.notifyPlayFromUri(packageName, pid, uid, caller, uri, extras);
+ } catch (RuntimeException e) {
Slog.e(TAG, "Remote failure in playFromUri.", e);
}
}
public void skipToTrack(String packageName, int pid, int uid,
- ISessionControllerCallback caller, long id) {
+ ControllerCallbackLink caller, long id) {
try {
- mCb.onSkipToTrack(packageName, pid, uid, caller, id);
- } catch (RemoteException e) {
+ mCb.notifySkipToTrack(packageName, pid, uid, caller, id);
+ } catch (RuntimeException e) {
Slog.e(TAG, "Remote failure in skipToTrack", e);
}
}
- public void pause(String packageName, int pid, int uid, ISessionControllerCallback caller) {
+ public void pause(String packageName, int pid, int uid, ControllerCallbackLink caller) {
try {
- mCb.onPause(packageName, pid, uid, caller);
- } catch (RemoteException e) {
+ mCb.notifyPause(packageName, pid, uid, caller);
+ } catch (RuntimeException e) {
Slog.e(TAG, "Remote failure in pause.", e);
}
}
- public void stop(String packageName, int pid, int uid, ISessionControllerCallback caller) {
+ public void stop(String packageName, int pid, int uid, ControllerCallbackLink caller) {
try {
- mCb.onStop(packageName, pid, uid, caller);
- } catch (RemoteException e) {
+ mCb.notifyStop(packageName, pid, uid, caller);
+ } catch (RuntimeException e) {
Slog.e(TAG, "Remote failure in stop.", e);
}
}
- public void next(String packageName, int pid, int uid, ISessionControllerCallback caller) {
+ public void next(String packageName, int pid, int uid, ControllerCallbackLink caller) {
try {
- mCb.onNext(packageName, pid, uid, caller);
- } catch (RemoteException e) {
+ mCb.notifyNext(packageName, pid, uid, caller);
+ } catch (RuntimeException e) {
Slog.e(TAG, "Remote failure in next.", e);
}
}
public void previous(String packageName, int pid, int uid,
- ISessionControllerCallback caller) {
+ ControllerCallbackLink caller) {
try {
- mCb.onPrevious(packageName, pid, uid, caller);
- } catch (RemoteException e) {
+ mCb.notifyPrevious(packageName, pid, uid, caller);
+ } catch (RuntimeException e) {
Slog.e(TAG, "Remote failure in previous.", e);
}
}
public void fastForward(String packageName, int pid, int uid,
- ISessionControllerCallback caller) {
+ ControllerCallbackLink caller) {
try {
- mCb.onFastForward(packageName, pid, uid, caller);
- } catch (RemoteException e) {
+ mCb.notifyFastForward(packageName, pid, uid, caller);
+ } catch (RuntimeException e) {
Slog.e(TAG, "Remote failure in fastForward.", e);
}
}
public void rewind(String packageName, int pid, int uid,
- ISessionControllerCallback caller) {
+ ControllerCallbackLink caller) {
try {
- mCb.onRewind(packageName, pid, uid, caller);
- } catch (RemoteException e) {
+ mCb.notifyRewind(packageName, pid, uid, caller);
+ } catch (RuntimeException e) {
Slog.e(TAG, "Remote failure in rewind.", e);
}
}
- public void seekTo(String packageName, int pid, int uid, ISessionControllerCallback caller,
+ public void seekTo(String packageName, int pid, int uid, ControllerCallbackLink caller,
long pos) {
try {
- mCb.onSeekTo(packageName, pid, uid, caller, pos);
- } catch (RemoteException e) {
+ mCb.notifySeekTo(packageName, pid, uid, caller, pos);
+ } catch (RuntimeException e) {
Slog.e(TAG, "Remote failure in seekTo.", e);
}
}
- public void rate(String packageName, int pid, int uid, ISessionControllerCallback caller,
+ public void rate(String packageName, int pid, int uid, ControllerCallbackLink caller,
Rating rating) {
try {
- mCb.onRate(packageName, pid, uid, caller, rating);
- } catch (RemoteException e) {
+ mCb.notifyRate(packageName, pid, uid, caller, rating);
+ } catch (RuntimeException e) {
Slog.e(TAG, "Remote failure in rate.", e);
}
}
public void adjustVolume(String packageName, int pid, int uid,
- ISessionControllerCallback caller, boolean asSystemService, int direction) {
+ ControllerCallbackLink caller, boolean asSystemService, int direction) {
try {
if (asSystemService) {
- mCb.onAdjustVolume(mContext.getPackageName(), Process.myPid(),
+ mCb.notifyAdjustVolume(mContext.getPackageName(), Process.myPid(),
Process.SYSTEM_UID, null, direction);
} else {
- mCb.onAdjustVolume(packageName, pid, uid, caller, direction);
+ mCb.notifyAdjustVolume(packageName, pid, uid, caller, direction);
}
- } catch (RemoteException e) {
+ } catch (RuntimeException e) {
Slog.e(TAG, "Remote failure in adjustVolume.", e);
}
}
public void setVolumeTo(String packageName, int pid, int uid,
- ISessionControllerCallback caller, int value) {
+ ControllerCallbackLink caller, int value) {
try {
- mCb.onSetVolumeTo(packageName, pid, uid, caller, value);
- } catch (RemoteException e) {
+ mCb.notifySetVolumeTo(packageName, pid, uid, caller, value);
+ } catch (RuntimeException e) {
Slog.e(TAG, "Remote failure in setVolumeTo.", e);
}
}
@@ -1161,34 +1184,34 @@
class ControllerStub extends ISessionController.Stub {
@Override
- public void sendCommand(String packageName, ISessionControllerCallback caller,
+ public void sendCommand(String packageName, ControllerCallbackLink caller,
String command, Bundle args, ResultReceiver cb) {
mSessionCb.sendCommand(packageName, Binder.getCallingPid(), Binder.getCallingUid(),
caller, command, args, cb);
}
@Override
- public boolean sendMediaButton(String packageName, ISessionControllerCallback cb,
+ public boolean sendMediaButton(String packageName, ControllerCallbackLink cb,
boolean asSystemService, KeyEvent keyEvent) {
return mSessionCb.sendMediaButton(packageName, Binder.getCallingPid(),
Binder.getCallingUid(), cb, asSystemService, keyEvent);
}
@Override
- public void registerCallbackListener(String packageName, ISessionControllerCallback cb) {
+ public void registerCallbackListener(String packageName, ControllerCallbackLink cb) {
synchronized (mLock) {
// If this session is already destroyed tell the caller and
// don't add them.
if (mDestroyed) {
try {
- cb.onSessionDestroyed();
+ cb.notifySessionDestroyed();
} catch (Exception e) {
// ignored
}
return;
}
if (getControllerHolderIndexForCb(cb) < 0) {
- mControllerCallbackHolders.add(new ISessionControllerCallbackHolder(cb,
+ mControllerCallbackHolders.add(new ControllerCallbackLinkHolder(cb,
packageName, Binder.getCallingUid()));
if (DEBUG) {
Log.d(TAG, "registering controller callback " + cb + " from controller"
@@ -1199,14 +1222,14 @@
}
@Override
- public void unregisterCallbackListener(ISessionControllerCallback cb) {
+ public void unregisterCallbackListener(ControllerCallbackLink cb) {
synchronized (mLock) {
int index = getControllerHolderIndexForCb(cb);
if (index != -1) {
mControllerCallbackHolders.remove(index);
}
if (DEBUG) {
- Log.d(TAG, "unregistering callback " + cb.asBinder());
+ Log.d(TAG, "unregistering callback " + cb.getBinder());
}
}
}
@@ -1253,7 +1276,7 @@
@Override
public void adjustVolume(String packageName, String opPackageName,
- ISessionControllerCallback caller, boolean asSystemService, int direction,
+ ControllerCallbackLink caller, boolean asSystemService, int direction,
int flags) {
int pid = Binder.getCallingPid();
int uid = Binder.getCallingUid();
@@ -1268,7 +1291,7 @@
@Override
public void setVolumeTo(String packageName, String opPackageName,
- ISessionControllerCallback caller, int value, int flags) {
+ ControllerCallbackLink caller, int value, int flags) {
int pid = Binder.getCallingPid();
int uid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
@@ -1281,110 +1304,110 @@
}
@Override
- public void prepare(String packageName, ISessionControllerCallback caller) {
+ public void prepare(String packageName, ControllerCallbackLink caller) {
mSessionCb.prepare(packageName, Binder.getCallingPid(), Binder.getCallingUid(), caller);
}
@Override
- public void prepareFromMediaId(String packageName, ISessionControllerCallback caller,
+ public void prepareFromMediaId(String packageName, ControllerCallbackLink caller,
String mediaId, Bundle extras) {
mSessionCb.prepareFromMediaId(packageName, Binder.getCallingPid(),
Binder.getCallingUid(), caller, mediaId, extras);
}
@Override
- public void prepareFromSearch(String packageName, ISessionControllerCallback caller,
+ public void prepareFromSearch(String packageName, ControllerCallbackLink caller,
String query, Bundle extras) {
mSessionCb.prepareFromSearch(packageName, Binder.getCallingPid(),
Binder.getCallingUid(), caller, query, extras);
}
@Override
- public void prepareFromUri(String packageName, ISessionControllerCallback caller,
+ public void prepareFromUri(String packageName, ControllerCallbackLink caller,
Uri uri, Bundle extras) {
mSessionCb.prepareFromUri(packageName, Binder.getCallingPid(), Binder.getCallingUid(),
caller, uri, extras);
}
@Override
- public void play(String packageName, ISessionControllerCallback caller) {
+ public void play(String packageName, ControllerCallbackLink caller) {
mSessionCb.play(packageName, Binder.getCallingPid(), Binder.getCallingUid(), caller);
}
@Override
- public void playFromMediaId(String packageName, ISessionControllerCallback caller,
+ public void playFromMediaId(String packageName, ControllerCallbackLink caller,
String mediaId, Bundle extras) {
mSessionCb.playFromMediaId(packageName, Binder.getCallingPid(), Binder.getCallingUid(),
caller, mediaId, extras);
}
@Override
- public void playFromSearch(String packageName, ISessionControllerCallback caller,
+ public void playFromSearch(String packageName, ControllerCallbackLink caller,
String query, Bundle extras) {
mSessionCb.playFromSearch(packageName, Binder.getCallingPid(), Binder.getCallingUid(),
caller, query, extras);
}
@Override
- public void playFromUri(String packageName, ISessionControllerCallback caller,
+ public void playFromUri(String packageName, ControllerCallbackLink caller,
Uri uri, Bundle extras) {
mSessionCb.playFromUri(packageName, Binder.getCallingPid(), Binder.getCallingUid(),
caller, uri, extras);
}
@Override
- public void skipToQueueItem(String packageName, ISessionControllerCallback caller,
+ public void skipToQueueItem(String packageName, ControllerCallbackLink caller,
long id) {
mSessionCb.skipToTrack(packageName, Binder.getCallingPid(), Binder.getCallingUid(),
caller, id);
}
@Override
- public void pause(String packageName, ISessionControllerCallback caller) {
+ public void pause(String packageName, ControllerCallbackLink caller) {
mSessionCb.pause(packageName, Binder.getCallingPid(), Binder.getCallingUid(), caller);
}
@Override
- public void stop(String packageName, ISessionControllerCallback caller) {
+ public void stop(String packageName, ControllerCallbackLink caller) {
mSessionCb.stop(packageName, Binder.getCallingPid(), Binder.getCallingUid(), caller);
}
@Override
- public void next(String packageName, ISessionControllerCallback caller) {
+ public void next(String packageName, ControllerCallbackLink caller) {
mSessionCb.next(packageName, Binder.getCallingPid(), Binder.getCallingUid(), caller);
}
@Override
- public void previous(String packageName, ISessionControllerCallback caller) {
+ public void previous(String packageName, ControllerCallbackLink caller) {
mSessionCb.previous(packageName, Binder.getCallingPid(), Binder.getCallingUid(),
caller);
}
@Override
- public void fastForward(String packageName, ISessionControllerCallback caller) {
+ public void fastForward(String packageName, ControllerCallbackLink caller) {
mSessionCb.fastForward(packageName, Binder.getCallingPid(), Binder.getCallingUid(),
caller);
}
@Override
- public void rewind(String packageName, ISessionControllerCallback caller) {
+ public void rewind(String packageName, ControllerCallbackLink caller) {
mSessionCb.rewind(packageName, Binder.getCallingPid(), Binder.getCallingUid(), caller);
}
@Override
- public void seekTo(String packageName, ISessionControllerCallback caller, long pos) {
+ public void seekTo(String packageName, ControllerCallbackLink caller, long pos) {
mSessionCb.seekTo(packageName, Binder.getCallingPid(), Binder.getCallingUid(), caller,
pos);
}
@Override
- public void rate(String packageName, ISessionControllerCallback caller, Rating rating) {
+ public void rate(String packageName, ControllerCallbackLink caller, Rating rating) {
mSessionCb.rate(packageName, Binder.getCallingPid(), Binder.getCallingUid(), caller,
rating);
}
@Override
- public void sendCustomAction(String packageName, ISessionControllerCallback caller,
+ public void sendCustomAction(String packageName, ControllerCallbackLink caller,
String action, Bundle args) {
mSessionCb.sendCustomAction(packageName, Binder.getCallingPid(), Binder.getCallingUid(),
caller, action, args);
@@ -1403,7 +1426,7 @@
}
@Override
- public ParceledListSlice getQueue() {
+ public List<QueueItem> getQueue() {
synchronized (mLock) {
return mQueue;
}
@@ -1432,12 +1455,12 @@
}
}
- private class ISessionControllerCallbackHolder {
- private final ISessionControllerCallback mCallback;
+ private class ControllerCallbackLinkHolder {
+ private final ControllerCallbackLink mCallback;
private final String mPackageName;
private final int mUid;
- ISessionControllerCallbackHolder(ISessionControllerCallback callback, String packageName,
+ ControllerCallbackLinkHolder(ControllerCallbackLink callback, String packageName,
int uid) {
mCallback = callback;
mPackageName = packageName;
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 7f2e047..ce0e72b 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -48,10 +48,10 @@
import android.media.session.IOnMediaKeyListener;
import android.media.session.IOnVolumeKeyLongPressListener;
import android.media.session.ISession;
-import android.media.session.ISessionCallback;
import android.media.session.ISessionManager;
import android.media.session.MediaSession;
import android.media.session.MediaSessionManager;
+import android.media.session.SessionCallbackLink;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
@@ -114,10 +114,11 @@
@GuardedBy("mLock")
private final ArrayList<SessionsListenerRecord> mSessionsListeners
= new ArrayList<SessionsListenerRecord>();
+ // Map user id as index to list of Session2Tokens
// TODO: Keep session2 info in MediaSessionStack for prioritizing both session1 and session2 in
// one place.
@GuardedBy("mLock")
- private final List<Session2Token> mSession2Tokens = new ArrayList<>();
+ private final SparseArray<List<Session2Token>> mSession2TokensPerUser = new SparseArray<>();
private KeyguardManager mKeyguardManager;
private IAudioService mAudioService;
@@ -305,10 +306,13 @@
updateUser();
}
+ // Called when the user with the userId is removed.
@Override
public void onStopUser(int userId) {
if (DEBUG) Log.d(TAG, "onStopUser: " + userId);
synchronized (mLock) {
+ // TODO: Also handle removing user in updateUser() because adding/switching user is
+ // handled in updateUser().
FullUserRecord user = getFullUserRecordLocked(userId);
if (user != null) {
if (user.mFullUserId == userId) {
@@ -318,6 +322,7 @@
user.destroySessionsForUserLocked(userId);
}
}
+ mSession2TokensPerUser.remove(userId);
updateUser();
}
}
@@ -363,6 +368,9 @@
mUserRecords.put(userInfo.id, new FullUserRecord(userInfo.id));
}
}
+ if (mSession2TokensPerUser.get(userInfo.id) == null) {
+ mSession2TokensPerUser.put(userInfo.id, new ArrayList<>());
+ }
}
}
// Ensure that the current full user exists.
@@ -372,6 +380,9 @@
Log.w(TAG, "Cannot find FullUserInfo for the current user " + currentFullUserId);
mCurrentFullUserRecord = new FullUserRecord(currentFullUserId);
mUserRecords.put(currentFullUserId, mCurrentFullUserRecord);
+ if (mSession2TokensPerUser.get(currentFullUserId) == null) {
+ mSession2TokensPerUser.put(currentFullUserId, new ArrayList<>());
+ }
}
mFullUserIds.put(currentFullUserId, currentFullUserId);
}
@@ -425,7 +436,7 @@
}
try {
- session.getCallback().asBinder().unlinkToDeath(session, 0);
+ session.getCallback().getBinder().unlinkToDeath(session, 0);
} catch (Exception e) {
// ignore exceptions while destroying a session.
}
@@ -511,7 +522,7 @@
}
private MediaSessionRecord createSessionInternal(int callerPid, int callerUid, int userId,
- String callerPackageName, ISessionCallback cb, String tag) throws RemoteException {
+ String callerPackageName, SessionCallbackLink cb, String tag) throws RemoteException {
synchronized (mLock) {
return createSessionLocked(callerPid, callerUid, userId, callerPackageName, cb, tag);
}
@@ -525,7 +536,7 @@
* 4. It needs to be added to the relevant user record.
*/
private MediaSessionRecord createSessionLocked(int callerPid, int callerUid, int userId,
- String callerPackageName, ISessionCallback cb, String tag) {
+ String callerPackageName, SessionCallbackLink cb, String tag) {
FullUserRecord user = getFullUserRecordLocked(userId);
if (user == null) {
Log.wtf(TAG, "Request from invalid user: " + userId);
@@ -535,7 +546,7 @@
final MediaSessionRecord session = new MediaSessionRecord(callerPid, callerUid, userId,
callerPackageName, cb, tag, this, mHandler.getLooper());
try {
- cb.asBinder().linkToDeath(session, 0);
+ cb.getBinder().linkToDeath(session, 0);
} catch (RemoteException e) {
throw new RuntimeException("Media Session owner died prematurely.", e);
}
@@ -732,9 +743,15 @@
pw.println(indent + "Restored MediaButtonReceiverComponentType: "
+ mRestoredMediaButtonReceiverComponentType);
mPriorityStack.dump(pw, indent);
- pw.println(indent + "Session2Tokens - " + mSession2Tokens.size());
- for (Session2Token session2Token : mSession2Tokens) {
- pw.println(indent + " " + session2Token);
+ pw.println(indent + "Session2Tokens:");
+ for (int i = 0; i < mSession2TokensPerUser.size(); i++) {
+ List<Session2Token> list = mSession2TokensPerUser.valueAt(i);
+ if (list == null || list.size() == 0) {
+ continue;
+ }
+ for (Session2Token token : list) {
+ pw.println(indent + " " + token);
+ }
}
}
@@ -890,7 +907,7 @@
private boolean mVoiceButtonHandled = false;
@Override
- public ISession createSession(String packageName, ISessionCallback cb, String tag,
+ public ISession createSession(String packageName, SessionCallbackLink cb, String tag,
int userId) throws RemoteException {
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
@@ -956,6 +973,34 @@
}
@Override
+ public List<Session2Token> getSession2Tokens(int userId) {
+ final int pid = Binder.getCallingPid();
+ final int uid = Binder.getCallingUid();
+ final long token = Binder.clearCallingIdentity();
+
+ try {
+ // Check that they can make calls on behalf of the user and
+ // get the final user id
+ int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
+ true /* allowAll */, true /* requireFull */, "getSession2Tokens",
+ null /* optional packageName */);
+ List<Session2Token> result = new ArrayList<>();
+ synchronized (mLock) {
+ if (resolvedUserId == UserHandle.USER_ALL) {
+ for (int i = 0; i < mSession2TokensPerUser.size(); i++) {
+ result.addAll(mSession2TokensPerUser.valueAt(i));
+ }
+ } else {
+ result.addAll(mSession2TokensPerUser.get(userId));
+ }
+ }
+ return result;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
public void addSessionsListener(IActiveSessionsListener listener,
ComponentName componentName, int userId) throws RemoteException {
final int pid = Binder.getCallingPid();
@@ -1965,14 +2010,16 @@
@Override
public void onConnected(MediaController2 controller, Session2CommandGroup allowedCommands) {
synchronized (mLock) {
- mSession2Tokens.add(mToken);
+ int userId = UserHandle.getUserId(mToken.getUid());
+ mSession2TokensPerUser.get(userId).add(mToken);
}
}
@Override
public void onDisconnected(MediaController2 controller) {
synchronized (mLock) {
- mSession2Tokens.remove(mToken);
+ int userId = UserHandle.getUserId(mToken.getUid());
+ mSession2TokensPerUser.get(userId).remove(mToken);
}
}
}
diff --git a/services/core/java/com/android/server/os/BugreportManagerService.java b/services/core/java/com/android/server/os/BugreportManagerService.java
new file mode 100644
index 0000000..e241591
--- /dev/null
+++ b/services/core/java/com/android/server/os/BugreportManagerService.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.os;
+
+import android.content.Context;
+
+import com.android.server.SystemService;
+
+/**
+ * Service that provides a privileged API to capture and consume bugreports.
+ *
+ * @hide
+ */
+public class BugreportManagerService extends SystemService {
+ private static final String TAG = "BugreportManagerService";
+
+ private BugreportManagerServiceImpl mService;
+
+ public BugreportManagerService(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onStart() {
+ mService = new BugreportManagerServiceImpl(getContext());
+ // TODO(b/111441001): Needs sepolicy to be submitted first.
+ // publishBinderService(Context.BUGREPORT_SERVICE, mService);
+ }
+}
diff --git a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
new file mode 100644
index 0000000..faa4714
--- /dev/null
+++ b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.os;
+
+import android.annotation.RequiresPermission;
+import android.content.Context;
+import android.os.BugreportParams;
+import android.os.IDumpstate;
+import android.os.IDumpstateListener;
+import android.os.IDumpstateToken;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.util.Slog;
+
+import java.io.FileDescriptor;
+
+// TODO(b/111441001):
+// 1. Handle the case where another bugreport is in progress
+// 2. Make everything threadsafe
+// 3. Pass validation & other errors on listener
+
+/**
+ * Implementation of the service that provides a privileged API to capture and consume bugreports.
+ *
+ * <p>Delegates the actualy generation to a native implementation of {@code Dumpstate}.
+ */
+class BugreportManagerServiceImpl extends IDumpstate.Stub {
+ private static final String TAG = "BugreportManagerService";
+ private static final long DEFAULT_BUGREPORT_SERVICE_TIMEOUT_MILLIS = 30 * 1000;
+
+ private IDumpstate mDs = null;
+ private final Context mContext;
+
+ BugreportManagerServiceImpl(Context context) {
+ mContext = context;
+ }
+
+ @Override
+ @RequiresPermission(android.Manifest.permission.DUMP)
+ public IDumpstateToken setListener(String name, IDumpstateListener listener,
+ boolean getSectionDetails) throws RemoteException {
+ // TODO(b/111441001): Figure out if lazy setting of listener should be allowed
+ // and if so how to handle it.
+ throw new UnsupportedOperationException("setListener is not allowed on this service");
+ }
+
+
+ @Override
+ @RequiresPermission(android.Manifest.permission.DUMP)
+ public void startBugreport(FileDescriptor bugreportFd, FileDescriptor screenshotFd,
+ int bugreportMode, IDumpstateListener listener) throws RemoteException {
+
+ validate(bugreportMode);
+
+ mDs = getDumpstateService();
+ if (mDs == null) {
+ Slog.w(TAG, "Unable to get bugreport service");
+ // TODO(b/111441001): pass error on listener
+ return;
+ }
+ mDs.startBugreport(bugreportFd, screenshotFd, bugreportMode, listener);
+ }
+
+ private boolean validate(@BugreportParams.BugreportMode int mode) {
+ if (mode != BugreportParams.BUGREPORT_MODE_FULL
+ && mode != BugreportParams.BUGREPORT_MODE_INTERACTIVE
+ && mode != BugreportParams.BUGREPORT_MODE_REMOTE
+ && mode != BugreportParams.BUGREPORT_MODE_WEAR
+ && mode != BugreportParams.BUGREPORT_MODE_TELEPHONY
+ && mode != BugreportParams.BUGREPORT_MODE_WIFI) {
+ Slog.w(TAG, "Unknown bugreport mode: " + mode);
+ return false;
+ }
+ return true;
+ }
+
+ /*
+ * Start and get a handle to the native implementation of {@code IDumpstate} which does the
+ * actual bugreport generation.
+ *
+ * <p>Generating bugreports requires root privileges. To limit the footprint
+ * of the root access, the actual generation in Dumpstate binary is accessed as a
+ * oneshot service 'bugreport'.
+ */
+ private IDumpstate getDumpstateService() {
+ // Start bugreport service.
+ SystemProperties.set("ctl.start", "bugreport");
+
+ IDumpstate ds = null;
+ boolean timedOut = false;
+ int totalTimeWaitedMillis = 0;
+ int seedWaitTimeMillis = 500;
+ while (!timedOut) {
+ // Note that the binder service on the native side is "dumpstate".
+ ds = IDumpstate.Stub.asInterface(ServiceManager.getService("dumpstate"));
+ if (ds != null) {
+ Slog.i(TAG, "Got bugreport service handle.");
+ break;
+ }
+ SystemClock.sleep(seedWaitTimeMillis);
+ Slog.i(TAG,
+ "Waiting to get dumpstate service handle (" + totalTimeWaitedMillis + "ms)");
+ totalTimeWaitedMillis += seedWaitTimeMillis;
+ seedWaitTimeMillis *= 2;
+ timedOut = totalTimeWaitedMillis > DEFAULT_BUGREPORT_SERVICE_TIMEOUT_MILLIS;
+ }
+ if (timedOut) {
+ Slog.w(TAG,
+ "Timed out waiting to get dumpstate service handle ("
+ + totalTimeWaitedMillis + "ms)");
+ }
+ return ds;
+ }
+}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 7ca39df..d0b20e8 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -329,6 +329,9 @@
if (valid) {
mSessions.put(session.sessionId, session);
+ if (session.isStaged()) {
+ mStagingManager.restoreSession(session);
+ }
} else {
// Since this is early during boot we don't send
// any observer events about the session, but we
@@ -533,7 +536,7 @@
session = new PackageInstallerSession(mInternalCallback, mContext, mPm, this,
mInstallThread.getLooper(), mStagingManager, sessionId, userId,
installerPackageName, callingUid, params, createdMillis, stageDir, stageCid, false,
- false, null, SessionInfo.INVALID_ID);
+ false, null, SessionInfo.INVALID_ID, false, false, false, SessionInfo.NO_ERROR);
synchronized (mSessions) {
mSessions.put(sessionId, session);
@@ -1131,7 +1134,9 @@
}
}
synchronized (mSessions) {
- mSessions.remove(session.sessionId);
+ if (!session.isStaged() || !success) {
+ mSessions.remove(session.sessionId);
+ }
addHistoricalSessionLocked(session);
final File appIconFile = buildAppIconFile(session.sessionId);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 5cb6c34..516927f 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -151,6 +151,10 @@
private static final String ATTR_MULTI_PACKAGE = "multiPackage";
private static final String ATTR_PARENT_SESSION_ID = "parentSessionId";
private static final String ATTR_STAGED_SESSION = "stagedSession";
+ private static final String ATTR_IS_READY = "isReady";
+ private static final String ATTR_IS_FAILED = "isFailed";
+ private static final String ATTR_IS_APPLIED = "isApplied";
+ private static final String ATTR_STAGED_SESSION_ERROR_CODE = "errorCode";
private static final String ATTR_MODE = "mode";
private static final String ATTR_INSTALL_FLAGS = "installFlags";
private static final String ATTR_INSTALL_LOCATION = "installLocation";
@@ -408,7 +412,8 @@
int sessionId, int userId,
String installerPackageName, int installerUid, SessionParams params, long createdMillis,
File stageDir, String stageCid, boolean prepared, boolean sealed,
- @Nullable int[] childSessionIds, int parentSessionId) {
+ @Nullable int[] childSessionIds, int parentSessionId, boolean isReady,
+ boolean isFailed, boolean isApplied, int stagedSessionErrorCode) {
mCallback = callback;
mContext = context;
mPm = pm;
@@ -438,7 +443,10 @@
}
mPrepared = prepared;
-
+ mStagedSessionReady = isReady;
+ mStagedSessionFailed = isFailed;
+ mStagedSessionApplied = isApplied;
+ mStagedSessionErrorCode = stagedSessionErrorCode;
if (sealed) {
synchronized (mLock) {
try {
@@ -2023,8 +2031,9 @@
private void destroyInternal() {
synchronized (mLock) {
mSealed = true;
- mDestroyed = true;
-
+ if (!params.isStaged) {
+ mDestroyed = true;
+ }
// Force shut down all bridges
for (RevocableFileDescriptor fd : mFds) {
fd.revoke();
@@ -2131,6 +2140,10 @@
writeBooleanAttribute(out, ATTR_MULTI_PACKAGE, params.isMultiPackage);
writeBooleanAttribute(out, ATTR_STAGED_SESSION, params.isStaged);
+ writeBooleanAttribute(out, ATTR_IS_READY, mStagedSessionReady);
+ writeBooleanAttribute(out, ATTR_IS_FAILED, mStagedSessionFailed);
+ writeBooleanAttribute(out, ATTR_IS_APPLIED, mStagedSessionApplied);
+ writeIntAttribute(out, ATTR_STAGED_SESSION_ERROR_CODE, mStagedSessionErrorCode);
// TODO(patb,109941548): avoid writing to xml and instead infer / validate this after
// we've read all sessions.
writeIntAttribute(out, ATTR_PARENT_SESSION_ID, mParentSessionId);
@@ -2269,10 +2282,16 @@
params.appIcon = BitmapFactory.decodeFile(appIconFile.getAbsolutePath());
params.appIconLastModified = appIconFile.lastModified();
}
+ final boolean isReady = readBooleanAttribute(in, ATTR_IS_READY);
+ final boolean isFailed = readBooleanAttribute(in, ATTR_IS_FAILED);
+ final boolean isApplied = readBooleanAttribute(in, ATTR_IS_APPLIED);
+ final int stagedSessionErrorCode = readIntAttribute(in, ATTR_STAGED_SESSION_ERROR_CODE);
+
return new PackageInstallerSession(callback, context, pm, sessionProvider,
installerThread, stagingManager, sessionId, userId, installerPackageName,
installerUid, params, createdMillis, stageDir, stageCid, prepared, sealed,
- EMPTY_CHILD_SESSION_ARRAY, parentSessionId);
+ EMPTY_CHILD_SESSION_ARRAY, parentSessionId, isReady, isFailed, isApplied,
+ stagedSessionErrorCode);
}
/**
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 48ee9dc..c297c62 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -53,9 +53,6 @@
private final PackageManagerService mPm;
private final Handler mBgHandler;
- // STOPSHIP: This is a temporary mock implementation of staged sessions. This variable
- // shouldn't be needed at all.
- // TODO(b/118865310): Implement staged sessions logic.
@GuardedBy("mStagedSessions")
private final SparseArray<PackageInstallerSession> mStagedSessions = new SparseArray<>();
@@ -191,4 +188,11 @@
mStagedSessions.remove(sessionInfo.sessionId);
}
}
+
+ void restoreSession(@NonNull PackageInstallerSession session) {
+ updateStoredSession(session);
+ // TODO(b/118865310): This method is called when PackageInstaller is re-instantiated, e.g.
+ // at reboot. Staging manager should at this point recover state from apexd and decide what
+ // to do with the session.
+ }
}
diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
index 2cab63a..ef77140 100644
--- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
+++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
@@ -510,7 +510,8 @@
Slog.d(LOG_TAG, "Binding to " + serviceIntent.getComponent());
willBind = mContext.bindServiceAsUser(
serviceIntent, mConnection,
- Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
+ Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE
+ | Context.BIND_RESTRICT_ASSOCIATIONS,
UserHandle.of(mUserId));
mBinding = willBind;
} finally {
diff --git a/services/core/java/com/android/server/textservices/TextServicesManagerService.java b/services/core/java/com/android/server/textservices/TextServicesManagerService.java
index 65d5b10..7236d79 100644
--- a/services/core/java/com/android/server/textservices/TextServicesManagerService.java
+++ b/services/core/java/com/android/server/textservices/TextServicesManagerService.java
@@ -16,8 +16,6 @@
package com.android.server.textservices;
-import static android.view.textservice.TextServicesManager.DISABLE_PER_PROFILE_SPELL_CHECKER;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -43,6 +41,7 @@
import android.text.TextUtils;
import android.util.Slog;
import android.util.SparseArray;
+import android.view.inputmethod.InputMethodSystemProperty;
import android.view.textservice.SpellCheckerInfo;
import android.view.textservice.SpellCheckerSubtype;
@@ -334,7 +333,7 @@
mContext = context;
mUserManager = mContext.getSystemService(UserManager.class);
mSpellCheckerOwnerUserIdMap = new LazyIntToIntMap(callingUserId -> {
- if (DISABLE_PER_PROFILE_SPELL_CHECKER) {
+ if (!InputMethodSystemProperty.PER_PROFILE_IME_ENABLED) {
final long token = Binder.clearCallingIdentity();
try {
final UserInfo parent = mUserManager.getProfileParent(callingUserId);
@@ -355,7 +354,7 @@
private void initializeInternalStateLocked(@UserIdInt int userId) {
// When DISABLE_PER_PROFILE_SPELL_CHECKER is true, we make sure here that work profile users
// will never have non-null TextServicesData for their user ID.
- if (DISABLE_PER_PROFILE_SPELL_CHECKER
+ if (!InputMethodSystemProperty.PER_PROFILE_IME_ENABLED
&& userId != mSpellCheckerOwnerUserIdMap.get(userId)) {
return;
}
@@ -780,7 +779,7 @@
private TextServicesData getDataFromCallingUserIdLocked(@UserIdInt int callingUserId) {
final int spellCheckerOwnerUserId = mSpellCheckerOwnerUserIdMap.get(callingUserId);
final TextServicesData data = mUserData.get(spellCheckerOwnerUserId);
- if (DISABLE_PER_PROFILE_SPELL_CHECKER) {
+ if (!InputMethodSystemProperty.PER_PROFILE_IME_ENABLED) {
if (spellCheckerOwnerUserId != callingUserId) {
// Calling process is running under child profile.
if (data == null) {
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 410f864..fcc8284 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -84,6 +84,7 @@
import android.system.ErrnoException;
import android.system.Os;
import android.util.EventLog;
+import android.util.FeatureFlagUtils;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
@@ -2219,8 +2220,12 @@
synchronized (mLock) {
mInAmbientMode = inAmbientMode;
final WallpaperData data = mWallpaperMap.get(mCurrentUserId);
- if (data != null && data.connection != null && data.connection.mInfo != null
- && data.connection.mInfo.supportsAmbientMode()) {
+ final boolean hasConnection = data != null && data.connection != null;
+ final WallpaperInfo info = hasConnection ? data.connection.mInfo : null;
+
+ // The wallpaper info is null for image wallpaper, also use the engine in this case.
+ if (hasConnection && (info == null && isAodImageWallpaperEnabled()
+ || info != null && info.supportsAmbientMode())) {
// TODO(multi-display) Extends this method with specific display.
engine = data.connection.getDisplayConnectorOrCreate(DEFAULT_DISPLAY).mEngine;
} else {
@@ -2237,6 +2242,10 @@
}
}
+ private boolean isAodImageWallpaperEnabled() {
+ return FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.AOD_IMAGEWALLPAPER_ENABLED);
+ }
+
@Override
public boolean setLockWallpaperCallback(IWallpaperManagerCallback cb) {
checkPermission(android.Manifest.permission.INTERNAL_SYSTEM_WINDOW);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 6f8f85f..f8f0d1c 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -390,6 +390,11 @@
private boolean mTurnScreenOn;
/**
+ * Current sequencing integer of the configuration, for skipping old activity configurations.
+ */
+ private int mConfigurationSeq;
+
+ /**
* Temp configs used in {@link #ensureActivityConfiguration(int, boolean)}
*/
private final Configuration mTmpConfig = new Configuration();
@@ -2568,6 +2573,45 @@
onRequestedOverrideConfigurationChanged(mTmpConfig);
}
+ @Override
+ void resolveOverrideConfiguration(Configuration newParentConfiguration) {
+ super.resolveOverrideConfiguration(newParentConfiguration);
+
+ // Assign configuration sequence number into hierarchy because there is a different way than
+ // ensureActivityConfiguration() in this class that uses configuration in WindowState during
+ // layout traversals.
+ mConfigurationSeq = Math.max(++mConfigurationSeq, 1);
+ getResolvedOverrideConfiguration().seq = mConfigurationSeq;
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newParentConfig) {
+ super.onConfigurationChanged(newParentConfig);
+
+ // Configuration's equality doesn't consider seq so if only seq number changes in resolved
+ // override configuration. Therefore ConfigurationContainer doesn't change merged override
+ // configuration, but it's used to push configuration changes so explicitly update that.
+ if (getMergedOverrideConfiguration().seq != getResolvedOverrideConfiguration().seq) {
+ onMergedOverrideConfigurationChanged();
+ }
+
+ // TODO(b/80414790): Remove code below after unification.
+ // Same as above it doesn't notify configuration listeners, and consequently AppWindowToken
+ // can't get updated seq number. However WindowState's merged override configuration needs
+ // to have this seq number because that's also used for activity config pushes during layout
+ // traversal. Therefore explicitly update them here.
+ if (mAppWindowToken == null) {
+ return;
+ }
+ final Configuration appWindowTokenRequestedOverrideConfig =
+ mAppWindowToken.getRequestedOverrideConfiguration();
+ if (appWindowTokenRequestedOverrideConfig.seq != getResolvedOverrideConfiguration().seq) {
+ appWindowTokenRequestedOverrideConfig.seq =
+ getResolvedOverrideConfiguration().seq;
+ mAppWindowToken.onMergedOverrideConfigurationChanged();
+ }
+ }
+
/** Returns true if the configuration is compatible with this activity. */
boolean isConfigurationCompatible(Configuration config) {
final int orientation = mAppWindowToken != null
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 7f6895a..76ae5cc 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -221,7 +221,7 @@
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.IAccessibilityManager;
import android.view.inputmethod.InputMethodInfo;
-import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.InputMethodSystemProperty;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
@@ -245,6 +245,7 @@
import com.android.server.SystemServerInitThreadPool;
import com.android.server.SystemService;
import com.android.server.devicepolicy.DevicePolicyManagerService.ActiveAdmin.TrustAgentInfo;
+import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.net.NetworkPolicyManagerInternal;
import com.android.server.pm.UserRestrictionsUtils;
import com.android.server.storage.DeviceStorageMonitorInternal;
@@ -5322,6 +5323,7 @@
}
final int callingUserId = mInjector.userHandleGetCallingUserId();
+ ComponentName adminComponent = null;
synchronized (getLockObject()) {
// Make sure the caller has any active admin with the right policy or
// the required permission.
@@ -5332,8 +5334,7 @@
android.Manifest.permission.LOCK_DEVICE);
final long ident = mInjector.binderClearCallingIdentity();
try {
- final ComponentName adminComponent = admin == null ?
- null : admin.info.getComponent();
+ adminComponent = admin == null ? null : admin.info.getComponent();
if (adminComponent != null) {
// For Profile Owners only, callers with only permission not allowed.
if ((flags & DevicePolicyManager.FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY) != 0) {
@@ -5385,6 +5386,7 @@
}
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.LOCK_NOW)
+ .setAdmin(adminComponent)
.setInt(flags)
.write();
}
@@ -6101,7 +6103,9 @@
synchronized (getLockObject()) {
delegates = getDelegatePackagesInternalLocked(scope, userId);
}
- if (delegates.size() != 1) {
+ if (delegates.size() == 0) {
+ return null;
+ } else if (delegates.size() > 1) {
Slog.wtf(LOG_TAG, "More than one delegate holds " + scope);
return null;
}
@@ -9188,21 +9192,15 @@
}
Preconditions.checkNotNull(who, "ComponentName is null");
- // TODO When InputMethodManager supports per user calls remove
- // this restriction.
- if (!checkCallerIsCurrentUserOrProfile()) {
+ // TODO When InputMethodManager supports per user calls remove this restriction.
+ if (!InputMethodSystemProperty.PER_PROFILE_IME_ENABLED
+ && !checkCallerIsCurrentUserOrProfile()) {
return false;
}
-
final int callingUserId = mInjector.userHandleGetCallingUserId();
if (packageList != null) {
- // InputMethodManager fetches input methods for current user.
- // So this can only be set when calling user is the current user
- // or parent is current user in case of managed profiles.
- InputMethodManager inputMethodManager =
- mContext.getSystemService(InputMethodManager.class);
- List<InputMethodInfo> enabledImes = inputMethodManager.getEnabledInputMethodList();
-
+ List<InputMethodInfo> enabledImes = InputMethodManagerInternal.get()
+ .getEnabledInputMethodListAsUser(callingUserId);
if (enabledImes != null) {
List<String> enabledPackages = new ArrayList<String>();
for (InputMethodInfo ime : enabledImes) {
@@ -9250,22 +9248,16 @@
@Override
public List getPermittedInputMethodsForCurrentUser() {
enforceManageUsers();
- UserInfo currentUser;
- try {
- currentUser = mInjector.getIActivityManager().getCurrentUser();
- } catch (RemoteException e) {
- Slog.e(LOG_TAG, "Failed to make remote calls to get current user", e);
- // Activity managed is dead, just allow all IMEs
- return null;
- }
- int userId = currentUser.id;
+ final int callingUserId = mInjector.userHandleGetCallingUserId();
synchronized (getLockObject()) {
List<String> result = null;
// If we have multiple profiles we return the intersection of the
// permitted lists. This can happen in cases where we have a device
// and profile owner.
- int[] profileIds = mUserManager.getProfileIdsWithDisabled(userId);
+ int[] profileIds = InputMethodSystemProperty.PER_PROFILE_IME_ENABLED
+ ? new int[]{callingUserId}
+ : mUserManager.getProfileIdsWithDisabled(callingUserId);
for (int profileId : profileIds) {
// Just loop though all admins, only device or profiles
// owners can have permitted lists set.
@@ -9286,9 +9278,8 @@
// If we have a permitted list add all system input methods.
if (result != null) {
- InputMethodManager inputMethodManager =
- mContext.getSystemService(InputMethodManager.class);
- List<InputMethodInfo> imes = inputMethodManager.getInputMethodList();
+ List<InputMethodInfo> imes =
+ InputMethodManagerInternal.get().getInputMethodListAsUser(callingUserId);
if (imes != null) {
for (InputMethodInfo ime : imes) {
ServiceInfo serviceInfo = ime.getServiceInfo();
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/OverlayPackagesProvider.java b/services/devicepolicy/java/com/android/server/devicepolicy/OverlayPackagesProvider.java
index d0ec0ee..699bec2 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/OverlayPackagesProvider.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/OverlayPackagesProvider.java
@@ -23,6 +23,7 @@
import static com.android.internal.util.Preconditions.checkNotNull;
import android.annotation.NonNull;
+import android.annotation.UserIdInt;
import android.app.admin.DeviceAdminReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -30,15 +31,13 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.ServiceManager;
import android.util.ArraySet;
import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodSystemProperty;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.view.IInputMethodManager;
+import com.android.server.inputmethod.InputMethodManagerInternal;
import java.util.Arrays;
import java.util.List;
@@ -53,18 +52,38 @@
protected static final String TAG = "OverlayPackagesProvider";
private final PackageManager mPm;
- private final IInputMethodManager mIInputMethodManager;
private final Context mContext;
+ private final Injector mInjector;
public OverlayPackagesProvider(Context context) {
- this(context, getIInputMethodManager());
+ this(context, new DefaultInjector());
}
@VisibleForTesting
- OverlayPackagesProvider(Context context, IInputMethodManager iInputMethodManager) {
+ interface Injector {
+ boolean isPerProfileImeEnabled();
+ @NonNull
+ List<InputMethodInfo> getInputMethodListAsUser(@UserIdInt int userId);
+ }
+
+ private static final class DefaultInjector implements Injector {
+ @Override
+ public boolean isPerProfileImeEnabled() {
+ return InputMethodSystemProperty.PER_PROFILE_IME_ENABLED;
+ }
+
+ @NonNull
+ @Override
+ public List<InputMethodInfo> getInputMethodListAsUser(@UserIdInt int userId) {
+ return InputMethodManagerInternal.get().getInputMethodListAsUser(userId);
+ }
+ }
+
+ @VisibleForTesting
+ OverlayPackagesProvider(Context context, Injector injector) {
mContext = context;
mPm = checkNotNull(context.getPackageManager());
- mIInputMethodManager = checkNotNull(iInputMethodManager);
+ mInjector = checkNotNull(injector);
}
/**
@@ -89,10 +108,12 @@
// Newly installed system apps are uninstalled when they are not required and are either
// disallowed or have a launcher icon.
nonRequiredApps.removeAll(getRequiredApps(provisioningAction, admin.getPackageName()));
- // Don't delete the system input method packages in case of Device owner provisioning.
- if (ACTION_PROVISION_MANAGED_DEVICE.equals(provisioningAction)
+ if (mInjector.isPerProfileImeEnabled()) {
+ nonRequiredApps.removeAll(getSystemInputMethods(userId));
+ } else if (ACTION_PROVISION_MANAGED_DEVICE.equals(provisioningAction)
|| ACTION_PROVISION_MANAGED_USER.equals(provisioningAction)) {
- nonRequiredApps.removeAll(getSystemInputMethods());
+ // Don't delete the system input method packages in case of Device owner provisioning.
+ nonRequiredApps.removeAll(getSystemInputMethods(userId));
}
nonRequiredApps.addAll(getDisallowedApps(provisioningAction));
return nonRequiredApps;
@@ -114,16 +135,8 @@
return apps;
}
- private Set<String> getSystemInputMethods() {
- // InputMethodManager is final so it cannot be mocked.
- // So, we're using IInputMethodManager directly because it can be mocked.
- final List<InputMethodInfo> inputMethods;
- try {
- inputMethods = mIInputMethodManager.getInputMethodList();
- } catch (RemoteException e) {
- // Should not happen
- return null;
- }
+ private Set<String> getSystemInputMethods(int userId) {
+ final List<InputMethodInfo> inputMethods = mInjector.getInputMethodListAsUser(userId);
final Set<String> systemInputMethods = new ArraySet<>();
for (InputMethodInfo inputMethodInfo : inputMethods) {
ApplicationInfo applicationInfo = inputMethodInfo.getServiceInfo().applicationInfo;
@@ -149,11 +162,6 @@
return disallowedApps;
}
- private static IInputMethodManager getIInputMethodManager() {
- final IBinder b = ServiceManager.getService(Context.INPUT_METHOD_SERVICE);
- return IInputMethodManager.Stub.asInterface(b);
- }
-
private Set<String> getRequiredAppsSet(String provisioningAction) {
final int resId;
switch (provisioningAction) {
diff --git a/services/ipmemorystore/Android.bp b/services/ipmemorystore/Android.bp
new file mode 100644
index 0000000..013cf56
--- /dev/null
+++ b/services/ipmemorystore/Android.bp
@@ -0,0 +1,4 @@
+java_library_static {
+ name: "services.ipmemorystore",
+ srcs: ["java/**/*.java"],
+}
diff --git a/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreService.java b/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreService.java
new file mode 100644
index 0000000..c9759bf
--- /dev/null
+++ b/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreService.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.net.ipmemorystore;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.net.IIpMemoryStore;
+import android.net.ipmemorystore.Blob;
+import android.net.ipmemorystore.IOnBlobRetrievedListener;
+import android.net.ipmemorystore.IOnL2KeyResponseListener;
+import android.net.ipmemorystore.IOnNetworkAttributesRetrieved;
+import android.net.ipmemorystore.IOnSameNetworkResponseListener;
+import android.net.ipmemorystore.IOnStatusListener;
+import android.net.ipmemorystore.NetworkAttributesParcelable;
+
+/**
+ * Implementation for the IP memory store.
+ * This component offers specialized services for network components to store and retrieve
+ * knowledge about networks, and provides intelligence that groups level 2 networks together
+ * into level 3 networks.
+ *
+ * @hide
+ */
+public class IpMemoryStoreService extends IIpMemoryStore.Stub {
+ final Context mContext;
+
+ public IpMemoryStoreService(@NonNull final Context context) {
+ mContext = context;
+ }
+
+ /**
+ * Store network attributes for a given L2 key.
+ *
+ * @param l2Key The L2 key for the L2 network. Clients that don't know or care about the L2
+ * key and only care about grouping can pass a unique ID here like the ones
+ * generated by {@code java.util.UUID.randomUUID()}, but keep in mind the low
+ * relevance of such a network will lead to it being evicted soon if it's not
+ * refreshed. Use findL2Key to try and find a similar L2Key to these attributes.
+ * @param attributes The attributes for this network.
+ * @param listener A listener to inform of the completion of this call, or null if the client
+ * is not interested in learning about success/failure.
+ * Through the listener, returns the L2 key. This is useful if the L2 key was not specified.
+ * If the call failed, the L2 key will be null.
+ */
+ @Override
+ public void storeNetworkAttributes(@NonNull final String l2Key,
+ @NonNull final NetworkAttributesParcelable attributes,
+ @Nullable final IOnStatusListener listener) {
+ // TODO : implement this
+ }
+
+ /**
+ * Store a binary blob associated with an L2 key and a name.
+ *
+ * @param l2Key The L2 key for this network.
+ * @param clientId The ID of the client.
+ * @param name The name of this data.
+ * @param data The data to store.
+ * @param listener The listener that will be invoked to return the answer, or null if the
+ * is not interested in learning about success/failure.
+ * Through the listener, returns a status to indicate success or failure.
+ */
+ @Override
+ public void storeBlob(@NonNull final String l2Key, @NonNull final String clientId,
+ @NonNull final String name, @NonNull final Blob data,
+ @Nullable final IOnStatusListener listener) {
+ // TODO : implement this
+ }
+
+ /**
+ * Returns the best L2 key associated with the attributes.
+ *
+ * This will find a record that would be in the same group as the passed attributes. This is
+ * useful to choose the key for storing a sample or private data when the L2 key is not known.
+ * If multiple records are group-close to these attributes, the closest match is returned.
+ * If multiple records have the same closeness, the one with the smaller (unicode codepoint
+ * order) L2 key is returned.
+ * If no record matches these attributes, null is returned.
+ *
+ * @param attributes The attributes of the network to find.
+ * @param listener The listener that will be invoked to return the answer.
+ * Through the listener, returns the L2 key if one matched, or null.
+ */
+ @Override
+ public void findL2Key(@NonNull final NetworkAttributesParcelable attributes,
+ @NonNull final IOnL2KeyResponseListener listener) {
+ // TODO : implement this
+ }
+
+ /**
+ * Returns whether, to the best of the store's ability to tell, the two specified L2 keys point
+ * to the same L3 network. Group-closeness is used to determine this.
+ *
+ * @param l2Key1 The key for the first network.
+ * @param l2Key2 The key for the second network.
+ * @param listener The listener that will be invoked to return the answer.
+ * Through the listener, a SameL3NetworkResponse containing the answer and confidence.
+ */
+ @Override
+ public void isSameNetwork(@NonNull final String l2Key1, @NonNull final String l2Key2,
+ @NonNull final IOnSameNetworkResponseListener listener) {
+ // TODO : implement this
+ }
+
+ /**
+ * Retrieve the network attributes for a key.
+ * If no record is present for this key, this will return null attributes.
+ *
+ * @param l2Key The key of the network to query.
+ * @param listener The listener that will be invoked to return the answer.
+ * Through the listener, returns the network attributes and the L2 key associated with
+ * the query.
+ */
+ @Override
+ public void retrieveNetworkAttributes(@NonNull final String l2Key,
+ @NonNull final IOnNetworkAttributesRetrieved listener) {
+ // TODO : implement this.
+ }
+
+ /**
+ * Retrieve previously stored private data.
+ * If no data was stored for this L2 key and name this will return null.
+ *
+ * @param l2Key The L2 key.
+ * @param clientId The id of the client that stored this data.
+ * @param name The name of the data.
+ * @param listener The listener that will be invoked to return the answer.
+ * Through the listener, returns the private data if any or null if none, with the L2 key
+ * and the name of the data associated with the query.
+ */
+ @Override
+ public void retrieveBlob(@NonNull final String l2Key, @NonNull final String clientId,
+ @NonNull final String name, @NonNull final IOnBlobRetrievedListener listener) {
+ // TODO : implement this.
+ }
+}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 8084c2b..8235ca3 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -102,10 +102,12 @@
import com.android.server.media.projection.MediaProjectionManagerService;
import com.android.server.net.NetworkPolicyManagerService;
import com.android.server.net.NetworkStatsService;
+import com.android.server.net.ipmemorystore.IpMemoryStoreService;
import com.android.server.net.watchlist.NetworkWatchlistService;
import com.android.server.notification.NotificationManagerService;
import com.android.server.oemlock.OemLockService;
import com.android.server.om.OverlayManagerService;
+import com.android.server.os.BugreportManagerService;
import com.android.server.os.DeviceIdentifiersPolicyService;
import com.android.server.os.SchedulingPolicyService;
import com.android.server.pm.BackgroundDexOptService;
@@ -786,6 +788,11 @@
traceBeginAndSlog("StartRollbackManagerService");
mSystemServiceManager.startService(RollbackManagerService.class);
traceEnd();
+
+ // Service to capture bugreports.
+ traceBeginAndSlog("StartBugreportManagerService");
+ mSystemServiceManager.startService(BugreportManagerService.class);
+ traceEnd();
}
/**
@@ -1179,6 +1186,15 @@
}
traceEnd();
+ traceBeginAndSlog("StartIpMemoryStoreService");
+ try {
+ ServiceManager.addService(Context.IP_MEMORY_STORE_SERVICE,
+ new IpMemoryStoreService(context));
+ } catch (Throwable e) {
+ reportWtf("starting IP Memory Store Service", e);
+ }
+ traceEnd();
+
traceBeginAndSlog("StartIpSecService");
try {
ipSecService = IpSecService.create(context);
@@ -2202,7 +2218,7 @@
windowManager.onSystemUiStarted();
}
- private static void traceBeginAndSlog(String name) {
+ private static void traceBeginAndSlog(@NonNull String name) {
Slog.i(TAG, name);
BOOT_TIMINGS_TRACE_LOG.traceBegin(name);
}
diff --git a/services/net/Android.bp b/services/net/Android.bp
index ae697b7..3b4d6a7 100644
--- a/services/net/Android.bp
+++ b/services/net/Android.bp
@@ -11,10 +11,10 @@
]
}
-// TODO: move to networking module with IpNeighborMonitor/ConnectivityPacketTracker and remove lib
-java_library {
- name: "frameworks-net-shared-utils",
+filegroup {
+ name: "services-networkstack-shared-srcs",
srcs: [
- "java/android/net/util/FdEventsReader.java",
+ "java/android/net/util/FdEventsReader.java", // TODO: move to NetworkStack with IpClient
+ "java/android/net/shared/*.java",
]
-}
\ No newline at end of file
+}
diff --git a/services/net/java/android/net/shared/NetworkMonitorUtils.java b/services/net/java/android/net/shared/NetworkMonitorUtils.java
new file mode 100644
index 0000000..463cf2a
--- /dev/null
+++ b/services/net/java/android/net/shared/NetworkMonitorUtils.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.shared;
+
+import android.content.Context;
+import android.net.NetworkCapabilities;
+import android.provider.Settings;
+
+/** @hide */
+public class NetworkMonitorUtils {
+
+ // Network conditions broadcast constants
+ public static final String ACTION_NETWORK_CONDITIONS_MEASURED =
+ "android.net.conn.NETWORK_CONDITIONS_MEASURED";
+ public static final String EXTRA_CONNECTIVITY_TYPE = "extra_connectivity_type";
+ public static final String EXTRA_NETWORK_TYPE = "extra_network_type";
+ public static final String EXTRA_RESPONSE_RECEIVED = "extra_response_received";
+ public static final String EXTRA_IS_CAPTIVE_PORTAL = "extra_is_captive_portal";
+ public static final String EXTRA_CELL_ID = "extra_cellid";
+ public static final String EXTRA_SSID = "extra_ssid";
+ public static final String EXTRA_BSSID = "extra_bssid";
+ /** real time since boot */
+ public static final String EXTRA_REQUEST_TIMESTAMP_MS = "extra_request_timestamp_ms";
+ public static final String EXTRA_RESPONSE_TIMESTAMP_MS = "extra_response_timestamp_ms";
+ public static final String PERMISSION_ACCESS_NETWORK_CONDITIONS =
+ "android.permission.ACCESS_NETWORK_CONDITIONS";
+
+ // TODO: once the URL is a resource overlay, remove and have the resource define the default
+ private static final String DEFAULT_HTTP_URL =
+ "http://connectivitycheck.gstatic.com/generate_204";
+
+ /**
+ * Get the captive portal server HTTP URL that is configured on the device.
+ */
+ public static String getCaptivePortalServerHttpUrl(Context context) {
+ final String settingUrl = Settings.Global.getString(
+ context.getContentResolver(),
+ Settings.Global.CAPTIVE_PORTAL_HTTP_URL);
+ return settingUrl != null ? settingUrl : DEFAULT_HTTP_URL;
+ }
+
+ /**
+ * Return whether validation is required for a network.
+ * @param dfltNetCap Default requested network capabilities.
+ * @param nc Network capabilities of the network to test.
+ */
+ public static boolean isValidationRequired(
+ NetworkCapabilities dfltNetCap, NetworkCapabilities nc) {
+ // TODO: Consider requiring validation for DUN networks.
+ return dfltNetCap.satisfiedByNetworkCapabilities(nc);
+ }
+}
diff --git a/services/net/java/android/net/shared/PrivateDnsConfig.java b/services/net/java/android/net/shared/PrivateDnsConfig.java
new file mode 100644
index 0000000..41e0bad
--- /dev/null
+++ b/services/net/java/android/net/shared/PrivateDnsConfig.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.shared;
+
+import android.net.InetAddresses;
+import android.net.PrivateDnsConfigParcel;
+import android.text.TextUtils;
+
+import java.net.InetAddress;
+import java.util.Arrays;
+
+/** @hide */
+public class PrivateDnsConfig {
+ public final boolean useTls;
+ public final String hostname;
+ public final InetAddress[] ips;
+
+ public PrivateDnsConfig() {
+ this(false);
+ }
+
+ public PrivateDnsConfig(boolean useTls) {
+ this.useTls = useTls;
+ this.hostname = "";
+ this.ips = new InetAddress[0];
+ }
+
+ public PrivateDnsConfig(String hostname, InetAddress[] ips) {
+ this.useTls = !TextUtils.isEmpty(hostname);
+ this.hostname = useTls ? hostname : "";
+ this.ips = (ips != null) ? ips : new InetAddress[0];
+ }
+
+ public PrivateDnsConfig(PrivateDnsConfig cfg) {
+ useTls = cfg.useTls;
+ hostname = cfg.hostname;
+ ips = cfg.ips;
+ }
+
+ /**
+ * Indicates whether this is a strict mode private DNS configuration.
+ */
+ public boolean inStrictMode() {
+ return useTls && !TextUtils.isEmpty(hostname);
+ }
+
+ @Override
+ public String toString() {
+ return PrivateDnsConfig.class.getSimpleName()
+ + "{" + useTls + ":" + hostname + "/" + Arrays.toString(ips) + "}";
+ }
+
+ /**
+ * Create a stable AIDL-compatible parcel from the current instance.
+ */
+ public PrivateDnsConfigParcel toParcel() {
+ final PrivateDnsConfigParcel parcel = new PrivateDnsConfigParcel();
+ parcel.hostname = hostname;
+
+ final String[] parceledIps = new String[ips.length];
+ for (int i = 0; i < ips.length; i++) {
+ parceledIps[i] = ips[i].getHostAddress();
+ }
+ parcel.ips = parceledIps;
+
+ return parcel;
+ }
+
+ /**
+ * Build a configuration from a stable AIDL-compatible parcel.
+ */
+ public static PrivateDnsConfig fromParcel(PrivateDnsConfigParcel parcel) {
+ final InetAddress[] ips = new InetAddress[parcel.ips.length];
+ for (int i = 0; i < ips.length; i++) {
+ ips[i] = InetAddresses.parseNumericAddress(parcel.ips[i]);
+ }
+
+ return new PrivateDnsConfig(parcel.hostname, ips);
+ }
+}
diff --git a/services/net/java/android/net/util/SharedLog.java b/services/net/java/android/net/util/SharedLog.java
index 8b7b59d..2cdb2b0 100644
--- a/services/net/java/android/net/util/SharedLog.java
+++ b/services/net/java/android/net/util/SharedLog.java
@@ -70,6 +70,10 @@
mComponent = component;
}
+ public String getTag() {
+ return mTag;
+ }
+
/**
* Create a SharedLog based on this log with an additional component prefix on each logged line.
*/
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/OverlayPackagesProviderTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/OverlayPackagesProviderTest.java
index 34edd9f..757a046 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/OverlayPackagesProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/OverlayPackagesProviderTest.java
@@ -32,7 +32,6 @@
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.res.Resources;
-import android.os.RemoteException;
import android.test.AndroidTestCase;
import android.test.mock.MockPackageManager;
import android.view.inputmethod.InputMethodInfo;
@@ -40,7 +39,6 @@
import androidx.test.InstrumentationRegistry;
import com.android.internal.R;
-import com.android.internal.view.IInputMethodManager;
import org.junit.Before;
import org.junit.Test;
@@ -61,8 +59,8 @@
private @Mock
Resources mResources;
- private @Mock
- IInputMethodManager mIInputMethodManager;
+ @Mock
+ private OverlayPackagesProvider.Injector mInjector;
private @Mock
Context mTestContext;
private Resources mRealResources;
@@ -81,6 +79,7 @@
InstrumentationRegistry.getTargetContext().getCacheDir());
setSystemInputMethods();
+ setIsPerProfileModeEnabled(false);
setRequiredAppsManagedDevice();
setVendorRequiredAppsManagedDevice();
setDisallowedAppsManagedDevice();
@@ -95,7 +94,7 @@
setVendorDisallowedAppsManagedUser();
mRealResources = InstrumentationRegistry.getTargetContext().getResources();
- mHelper = new OverlayPackagesProvider(mTestContext, mIInputMethodManager);
+ mHelper = new OverlayPackagesProvider(mTestContext, mInjector);
}
@Test
@@ -165,6 +164,15 @@
}
@Test
+ public void testProfileOwnerImesAreRequiredForPerProfileImeMode() {
+ setSystemAppsWithLauncher("app.a", "app.b");
+ setSystemInputMethods("app.a");
+ setIsPerProfileModeEnabled(true);
+
+ verifyAppsAreNonRequired(ACTION_PROVISION_MANAGED_PROFILE, "app.b");
+ }
+
+ @Test
public void testManagedUserImesAreRequired() {
setSystemAppsWithLauncher("app.a", "app.b");
setSystemInputMethods("app.a");
@@ -333,11 +341,11 @@
InputMethodInfo inputMethodInfo = new InputMethodInfo(ri, false, null, null, 0, false);
inputMethods.add(inputMethodInfo);
}
- try {
- when(mIInputMethodManager.getInputMethodList()).thenReturn(inputMethods);
- } catch (RemoteException e) {
- fail(e.toString());
- }
+ when(mInjector.getInputMethodListAsUser(eq(TEST_USER_ID))).thenReturn(inputMethods);
+ }
+
+ private void setIsPerProfileModeEnabled(boolean enabled) {
+ when(mInjector.isPerProfileImeEnabled()).thenReturn(enabled);
}
private void setSystemAppsWithLauncher(String... apps) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index b6f1817..8f9d2ba 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -20,6 +20,9 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_BOTTOM;
import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_LEFT;
@@ -38,6 +41,7 @@
import android.app.ActivityOptions;
import android.app.servertransaction.ClientTransaction;
import android.app.servertransaction.PauseActivityItem;
+import android.content.res.Configuration;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.util.MutableBoolean;
@@ -210,4 +214,41 @@
assertNull(mActivity.pendingOptions);
assertNotNull(activity2.pendingOptions);
}
+
+ @Test
+ public void testNewOverrideConfigurationIncrementsSeq() {
+ final Configuration newConfig = new Configuration();
+
+ final int prevSeq = mActivity.getMergedOverrideConfiguration().seq;
+ mActivity.onRequestedOverrideConfigurationChanged(newConfig);
+ assertEquals(prevSeq + 1, mActivity.getMergedOverrideConfiguration().seq);
+ }
+
+ @Test
+ public void testNewParentConfigurationIncrementsSeq() {
+ final Configuration newConfig = new Configuration(
+ mTask.getRequestedOverrideConfiguration());
+ newConfig.orientation = newConfig.orientation == Configuration.ORIENTATION_PORTRAIT
+ ? Configuration.ORIENTATION_LANDSCAPE : Configuration.ORIENTATION_PORTRAIT;
+
+ final int prevSeq = mActivity.getMergedOverrideConfiguration().seq;
+ mTask.onRequestedOverrideConfigurationChanged(newConfig);
+ assertEquals(prevSeq + 1, mActivity.getMergedOverrideConfiguration().seq);
+ }
+
+ @Test
+ public void testNotifiesSeqIncrementToAppToken() {
+ final Configuration appWindowTokenRequestedOrientation = mock(Configuration.class);
+ mActivity.mAppWindowToken = mock(AppWindowToken.class);
+ doReturn(appWindowTokenRequestedOrientation).when(mActivity.mAppWindowToken)
+ .getRequestedOverrideConfiguration();
+
+ final Configuration newConfig = new Configuration();
+ newConfig.orientation = Configuration.ORIENTATION_PORTRAIT;
+
+ final int prevSeq = mActivity.getMergedOverrideConfiguration().seq;
+ mActivity.onRequestedOverrideConfigurationChanged(newConfig);
+ assertEquals(prevSeq + 1, appWindowTokenRequestedOrientation.seq);
+ verify(mActivity.mAppWindowToken).onMergedOverrideConfigurationChanged();
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
index bd29d2a..6c5cd5c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -264,6 +264,8 @@
.setOrientation(anyInt(), any(), any());
doCallRealMethod().when(activity.mAppWindowToken).setOrientation(anyInt());
doNothing().when(activity).removeWindowContainer();
+ doReturn(mock(Configuration.class)).when(activity.mAppWindowToken)
+ .getRequestedOverrideConfiguration();
if (mTaskRecord != null) {
mTaskRecord.addActivityToTop(activity);
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index a1e8b19..51d5ab1 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -174,6 +174,16 @@
private boolean mIsGroupDisabled = false;
/**
+ * Profile class, PROFILE_CLASS_TESTING, PROFILE_CLASS_OPERATIONAL
+ * PROFILE_CLASS_PROVISIONING, or PROFILE_CLASS_UNSET.
+ * A profile on the eUICC can be defined as test, operational, provisioning, or unset.
+ * The profile class will be populated from the profile metadata if present. Otherwise,
+ * the profile class defaults to unset if there is no profile metadata or the subscription
+ * is not on an eUICC ({@link #isEmbedded} returns false).
+ */
+ private int mProfileClass;
+
+ /**
* @hide
*/
public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
@@ -182,7 +192,8 @@
@Nullable UiccAccessRule[] accessRules, String cardString) {
this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number,
roaming, icon, mcc, mnc, countryIso, isEmbedded, accessRules, cardString,
- false, null, true, TelephonyManager.UNKNOWN_CARRIER_ID);
+ false, null, true, TelephonyManager.UNKNOWN_CARRIER_ID,
+ SubscriptionManager.PROFILE_CLASS_DEFAULT);
}
/**
@@ -192,10 +203,10 @@
CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
Bitmap icon, String mcc, String mnc, String countryIso, boolean isEmbedded,
@Nullable UiccAccessRule[] accessRules, String cardString, boolean isOpportunistic,
- @Nullable String groupUUID, boolean isMetered, int carrierId) {
+ @Nullable String groupUUID, boolean isMetered, int carrierId, int profileClass) {
this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number,
roaming, icon, mcc, mnc, countryIso, isEmbedded, accessRules, cardString, -1,
- isOpportunistic, groupUUID, isMetered, false, carrierId);
+ isOpportunistic, groupUUID, isMetered, false, carrierId, profileClass);
}
/**
@@ -206,7 +217,7 @@
Bitmap icon, String mcc, String mnc, String countryIso, boolean isEmbedded,
@Nullable UiccAccessRule[] accessRules, String cardString, int cardId,
boolean isOpportunistic, @Nullable String groupUUID, boolean isMetered,
- boolean isGroupDisabled, int carrierid) {
+ boolean isGroupDisabled, int carrierid, int profileClass) {
this.mId = id;
this.mIccId = iccId;
this.mSimSlotIndex = simSlotIndex;
@@ -229,6 +240,7 @@
this.mIsMetered = isMetered;
this.mIsGroupDisabled = isGroupDisabled;
this.mCarrierId = carrierid;
+ this.mProfileClass = profileClass;
}
@@ -466,6 +478,15 @@
}
/**
+ * @return the profile class of this subscription.
+ * @hide
+ */
+ @SystemApi
+ public @SubscriptionManager.ProfileClass int getProfileClass() {
+ return this.mProfileClass;
+ }
+
+ /**
* Checks whether the app with the given context is authorized to manage this subscription
* according to its metadata. Only supported for embedded subscriptions (if {@link #isEmbedded}
* returns true).
@@ -590,11 +611,12 @@
boolean isMetered = source.readBoolean();
boolean isGroupDisabled = source.readBoolean();
int carrierid = source.readInt();
+ int profileClass = source.readInt();
return new SubscriptionInfo(id, iccId, simSlotIndex, displayName, carrierName,
nameSource, iconTint, number, dataRoaming, iconBitmap, mcc, mnc, countryIso,
isEmbedded, accessRules, cardString, cardId, isOpportunistic, groupUUID,
- isMetered, isGroupDisabled, carrierid);
+ isMetered, isGroupDisabled, carrierid, profileClass);
}
@Override
@@ -627,6 +649,7 @@
dest.writeBoolean(mIsMetered);
dest.writeBoolean(mIsGroupDisabled);
dest.writeInt(mCarrierId);
+ dest.writeInt(mProfileClass);
}
@Override
@@ -662,7 +685,8 @@
+ " accessRules " + Arrays.toString(mAccessRules)
+ " cardString=" + cardStringToPrint + " cardId=" + mCardId
+ " isOpportunistic " + mIsOpportunistic + " mGroupUUID=" + mGroupUUID
- + " isMetered=" + mIsMetered + " mIsGroupDisabled=" + mIsGroupDisabled + "}";
+ + " isMetered=" + mIsMetered + " mIsGroupDisabled=" + mIsGroupDisabled
+ + " profileClass=" + mProfileClass + "}";
}
@Override
@@ -670,7 +694,7 @@
return Objects.hash(mId, mSimSlotIndex, mNameSource, mIconTint, mDataRoaming, mIsEmbedded,
mIsOpportunistic, mGroupUUID, mIsMetered, mIccId, mNumber, mMcc, mMnc,
mCountryIso, mCardString, mCardId, mDisplayName, mCarrierName, mAccessRules,
- mIsGroupDisabled, mCarrierId);
+ mIsGroupDisabled, mCarrierId, mProfileClass);
}
@Override
@@ -705,6 +729,7 @@
&& Objects.equals(mCardId, toCompare.mCardId)
&& TextUtils.equals(mDisplayName, toCompare.mDisplayName)
&& TextUtils.equals(mCarrierName, toCompare.mCarrierName)
- && Arrays.equals(mAccessRules, toCompare.mAccessRules);
+ && Arrays.equals(mAccessRules, toCompare.mAccessRules)
+ && mProfileClass == toCompare.mProfileClass;
}
}
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 2c712a1..34f7abd 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -22,6 +22,7 @@
import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.DurationMillisLong;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -62,6 +63,8 @@
import com.android.internal.telephony.ITelephonyRegistry;
import com.android.internal.telephony.PhoneConstants;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -599,6 +602,73 @@
* @hide
*/
public static final String IS_METERED = "is_metered";
+
+ /**
+ * TelephonyProvider column name for the profile class of a subscription
+ * Only present if {@link #IS_EMBEDDED} is 1.
+ * <P>Type: INTEGER (int)</P>
+ * @hide
+ */
+ public static final String PROFILE_CLASS = "profile_class";
+
+ /**
+ * Profile class of the subscription
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "PROFILE_CLASS_" }, value = {
+ PROFILE_CLASS_TESTING,
+ PROFILE_CLASS_PROVISIONING,
+ PROFILE_CLASS_OPERATIONAL,
+ PROFILE_CLASS_UNSET,
+ PROFILE_CLASS_DEFAULT
+ })
+ public @interface ProfileClass {}
+
+ /**
+ * A testing profile can be pre-loaded or downloaded onto
+ * the eUICC and provides connectivity to test equipment
+ * for the purpose of testing the device and the eUICC. It
+ * is not intended to store any operator credentials.
+ * @hide
+ */
+ @SystemApi
+ public static final int PROFILE_CLASS_TESTING = 0;
+
+ /**
+ * A provisioning profile is pre-loaded onto the eUICC and
+ * provides connectivity to a mobile network solely for the
+ * purpose of provisioning profiles.
+ * @hide
+ */
+ @SystemApi
+ public static final int PROFILE_CLASS_PROVISIONING = 1;
+
+ /**
+ * An operational profile can be pre-loaded or downloaded
+ * onto the eUICC and provides services provided by the
+ * operator.
+ * @hide
+ */
+ @SystemApi
+ public static final int PROFILE_CLASS_OPERATIONAL = 2;
+
+ /**
+ * The profile class is unset. This occurs when profile class
+ * info is not available. The subscription either has no profile
+ * metadata or the profile metadata did not encode profile class.
+ * @hide
+ */
+ @SystemApi
+ public static final int PROFILE_CLASS_UNSET = -1;
+
+ /**
+ * Default profile class
+ * @hide
+ */
+ @SystemApi
+ public static final int PROFILE_CLASS_DEFAULT = PROFILE_CLASS_UNSET;
+
/**
* Broadcast Action: The user has changed one of the default subs related to
* data, phone calls, or sms</p>
@@ -1102,17 +1172,33 @@
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
public List<SubscriptionInfo> getActiveSubscriptionInfoList() {
- List<SubscriptionInfo> result = null;
+ return getActiveSubscriptionInfoList(false);
+ }
+
+ /**
+ * This is similar to {@link #getActiveSubscriptionInfoList()}, but if userVisibleOnly
+ * is true, it will filter out the hidden subscriptions.
+ *
+ * @hide
+ */
+ public List<SubscriptionInfo> getActiveSubscriptionInfoList(boolean userVisibleOnly) {
+ List<SubscriptionInfo> activeList = null;
try {
ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
if (iSub != null) {
- result = iSub.getActiveSubscriptionInfoList(mContext.getOpPackageName());
+ activeList = iSub.getActiveSubscriptionInfoList(mContext.getOpPackageName());
}
} catch (RemoteException ex) {
// ignore it
}
- return result;
+
+ if (!userVisibleOnly || activeList == null) {
+ return activeList;
+ } else {
+ return activeList.stream().filter(subInfo -> !shouldHideSubscription(subInfo))
+ .collect(Collectors.toList());
+ }
}
/**
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index 6326cc6..cc9befe 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -616,14 +616,13 @@
/**
* Update the nickname for the given subscription.
*
- * <p>Requires that the calling app has the
- * {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission. This is for
- * internal system use only.
+ * <p>Requires that the calling app has carrier privileges according to the metadata of the
+ * profile to be updated, or the
+ * {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
*
* @param subscriptionId the ID of the subscription to update.
* @param nickname the new nickname to apply.
* @param callbackIntent a PendingIntent to launch when the operation completes.
- * @hide
*/
@RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
public void updateSubscriptionNickname(
@@ -634,7 +633,7 @@
}
try {
getIEuiccController().updateSubscriptionNickname(
- subscriptionId, nickname, callbackIntent);
+ subscriptionId, nickname, mContext.getOpPackageName(), callbackIntent);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl b/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
index 870a689..dd40d56 100644
--- a/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
@@ -39,7 +39,7 @@
oneway void switchToSubscription(int subscriptionId, String callingPackage,
in PendingIntent callbackIntent);
oneway void updateSubscriptionNickname(int subscriptionId, String nickname,
- in PendingIntent callbackIntent);
+ String callingPackage, in PendingIntent callbackIntent);
oneway void eraseSubscriptions(in PendingIntent callbackIntent);
oneway void retainSubscriptionsForFactoryReset(in PendingIntent callbackIntent);
}
\ No newline at end of file
diff --git a/test-mock/Android.bp b/test-mock/Android.bp
index 37158e5..e1d6e01 100644
--- a/test-mock/Android.bp
+++ b/test-mock/Android.bp
@@ -25,7 +25,8 @@
"android.test.mock",
],
+ srcs_lib: "framework",
+ srcs_lib_whitelist_dirs: ["core/java"],
srcs_lib_whitelist_pkgs: ["android"],
- metalava_enabled: false,
compile_dex: true,
}
diff --git a/tests/PackageWatchdog/Android.mk b/tests/PackageWatchdog/Android.mk
new file mode 100644
index 0000000..b53f27d
--- /dev/null
+++ b/tests/PackageWatchdog/Android.mk
@@ -0,0 +1,34 @@
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+# PackageWatchdogTest
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_PACKAGE_NAME := PackageWatchdogTest
+LOCAL_MODULE_TAGS := tests
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ junit \
+ frameworks-base-testutils \
+ android-support-test \
+ services
+
+LOCAL_JAVA_LIBRARIES := \
+ android.test.runner
+
+LOCAL_PRIVATE_PLATFORM_APIS := true
+LOCAL_COMPATIBILITY_SUITE := device-tests
+
+include $(BUILD_PACKAGE)
diff --git a/tests/PackageWatchdog/AndroidManifest.xml b/tests/PackageWatchdog/AndroidManifest.xml
new file mode 100644
index 0000000..fa89528
--- /dev/null
+++ b/tests/PackageWatchdog/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.tests.packagewatchdog" >
+
+ <application android:debuggable="true">
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.tests.packagewatchdog"
+ android:label="PackageWatchdog Test"/>
+</manifest>
diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
new file mode 100644
index 0000000..ec07037
--- /dev/null
+++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import static com.android.server.PackageWatchdog.TRIGGER_FAILURE_COUNT;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.os.test.TestLooper;
+import android.support.test.InstrumentationRegistry;
+
+import com.android.server.PackageWatchdog.PackageHealthObserver;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+// TODO(zezeozue): Write test without using PackageWatchdog#getPackages. Just rely on
+// behavior of observers receiving crash notifications or not to determine if it's registered
+/**
+ * Test PackageWatchdog.
+ */
+public class PackageWatchdogTest {
+ private static final String APP_A = "com.package.a";
+ private static final String APP_B = "com.package.b";
+ private static final String OBSERVER_NAME_1 = "observer1";
+ private static final String OBSERVER_NAME_2 = "observer2";
+ private static final String OBSERVER_NAME_3 = "observer3";
+ private static final long SHORT_DURATION = TimeUnit.SECONDS.toMillis(1);
+ private static final long LONG_DURATION = TimeUnit.SECONDS.toMillis(5);
+ private TestLooper mTestLooper;
+
+ @Before
+ public void setUp() throws Exception {
+ mTestLooper = new TestLooper();
+ mTestLooper.startAutoDispatch();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ new File(InstrumentationRegistry.getContext().getFilesDir(),
+ "package-watchdog.xml").delete();
+ }
+
+ /**
+ * Test registration, unregistration, package expiry and duration reduction
+ */
+ @Test
+ public void testRegistration() throws Exception {
+ PackageWatchdog watchdog = createWatchdog();
+ TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
+ TestObserver observer2 = new TestObserver(OBSERVER_NAME_2);
+ TestObserver observer3 = new TestObserver(OBSERVER_NAME_3);
+
+ // Start observing for observer1 which will be unregistered
+ watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION);
+ // Start observing for observer2 which will expire
+ watchdog.startObservingHealth(observer2, Arrays.asList(APP_A, APP_B), SHORT_DURATION);
+ // Start observing for observer3 which will have expiry duration reduced
+ watchdog.startObservingHealth(observer3, Arrays.asList(APP_A), LONG_DURATION);
+
+ // Verify packages observed at start
+ // 1
+ assertEquals(1, watchdog.getPackages(observer1).size());
+ assertTrue(watchdog.getPackages(observer1).contains(APP_A));
+ // 2
+ assertEquals(2, watchdog.getPackages(observer2).size());
+ assertTrue(watchdog.getPackages(observer2).contains(APP_A));
+ assertTrue(watchdog.getPackages(observer2).contains(APP_B));
+ // 3
+ assertEquals(1, watchdog.getPackages(observer3).size());
+ assertTrue(watchdog.getPackages(observer3).contains(APP_A));
+
+ // Then unregister observer1
+ watchdog.unregisterHealthObserver(observer1);
+
+ // Verify observer2 and observer3 left
+ // 1
+ assertNull(watchdog.getPackages(observer1));
+ // 2
+ assertEquals(2, watchdog.getPackages(observer2).size());
+ assertTrue(watchdog.getPackages(observer2).contains(APP_A));
+ assertTrue(watchdog.getPackages(observer2).contains(APP_B));
+ // 3
+ assertEquals(1, watchdog.getPackages(observer3).size());
+ assertTrue(watchdog.getPackages(observer3).contains(APP_A));
+
+ // Then advance time a little and run messages in Handlers so observer2 expires
+ Thread.sleep(SHORT_DURATION);
+ mTestLooper.dispatchAll();
+
+ // Verify observer3 left with reduced expiry duration
+ // 1
+ assertNull(watchdog.getPackages(observer1));
+ // 2
+ assertNull(watchdog.getPackages(observer2));
+ // 3
+ assertEquals(1, watchdog.getPackages(observer3).size());
+ assertTrue(watchdog.getPackages(observer3).contains(APP_A));
+
+ // Then advance time some more and run messages in Handlers so observer3 expires
+ Thread.sleep(LONG_DURATION);
+ mTestLooper.dispatchAll();
+
+ // Verify observer3 expired
+ // 1
+ assertNull(watchdog.getPackages(observer1));
+ // 2
+ assertNull(watchdog.getPackages(observer2));
+ // 3
+ assertNull(watchdog.getPackages(observer3));
+ }
+
+ /**
+ * Test package observers are persisted and loaded on startup
+ */
+ @Test
+ public void testPersistence() throws Exception {
+ PackageWatchdog watchdog1 = createWatchdog();
+ TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
+ TestObserver observer2 = new TestObserver(OBSERVER_NAME_2);
+
+ watchdog1.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog1.startObservingHealth(observer2, Arrays.asList(APP_A, APP_B), SHORT_DURATION);
+
+ // Verify 2 observers are registered and saved internally
+ // 1
+ assertEquals(1, watchdog1.getPackages(observer1).size());
+ assertTrue(watchdog1.getPackages(observer1).contains(APP_A));
+ // 2
+ assertEquals(2, watchdog1.getPackages(observer2).size());
+ assertTrue(watchdog1.getPackages(observer2).contains(APP_A));
+ assertTrue(watchdog1.getPackages(observer2).contains(APP_B));
+
+
+ // Then advance time and run IO Handler so file is saved
+ mTestLooper.dispatchAll();
+
+ // Then start a new watchdog
+ PackageWatchdog watchdog2 = createWatchdog();
+
+ // Verify the new watchdog loads observers on startup but nothing registered
+ assertEquals(0, watchdog2.getPackages(observer1).size());
+ assertEquals(0, watchdog2.getPackages(observer2).size());
+ // Verify random observer not saved returns null
+ assertNull(watchdog2.getPackages(new TestObserver(OBSERVER_NAME_3)));
+
+ // Then regiser observer1
+ watchdog2.registerHealthObserver(observer1);
+ watchdog2.registerHealthObserver(observer2);
+
+ // Verify 2 observers are registered after reload
+ // 1
+ assertEquals(1, watchdog1.getPackages(observer1).size());
+ assertTrue(watchdog1.getPackages(observer1).contains(APP_A));
+ // 2
+ assertEquals(2, watchdog1.getPackages(observer2).size());
+ assertTrue(watchdog1.getPackages(observer2).contains(APP_A));
+ assertTrue(watchdog1.getPackages(observer2).contains(APP_B));
+ }
+
+ /**
+ * Test package failure under threshold does not notify observers
+ */
+ @Test
+ public void testNoPackageFailureBeforeThreshold() throws Exception {
+ PackageWatchdog watchdog = createWatchdog();
+ TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
+ TestObserver observer2 = new TestObserver(OBSERVER_NAME_2);
+
+ watchdog.startObservingHealth(observer2, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION);
+
+ // Then fail APP_A below the threshold
+ for (int i = 0; i < TRIGGER_FAILURE_COUNT - 1; i++) {
+ watchdog.onPackageFailure(new String[]{APP_A});
+ }
+
+ // Verify that observers are not notified
+ assertEquals(0, observer1.mFailedPackages.size());
+ assertEquals(0, observer2.mFailedPackages.size());
+ }
+
+ /**
+ * Test package failure and notifies all observer since none handles the failure
+ */
+ @Test
+ public void testPackageFailureNotifyAll() throws Exception {
+ PackageWatchdog watchdog = createWatchdog();
+ TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
+ TestObserver observer2 = new TestObserver(OBSERVER_NAME_2);
+
+ // Start observing for observer1 and observer2 without handling failures
+ watchdog.startObservingHealth(observer2, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.startObservingHealth(observer1, Arrays.asList(APP_A, APP_B), SHORT_DURATION);
+
+ // Then fail APP_A and APP_B above the threshold
+ for (int i = 0; i < TRIGGER_FAILURE_COUNT; i++) {
+ watchdog.onPackageFailure(new String[]{APP_A, APP_B});
+ }
+
+ // Verify all observers are notifed of all package failures
+ List<String> observer1Packages = observer1.mFailedPackages;
+ List<String> observer2Packages = observer2.mFailedPackages;
+ assertEquals(2, observer1Packages.size());
+ assertEquals(1, observer2Packages.size());
+ assertEquals(APP_A, observer1Packages.get(0));
+ assertEquals(APP_B, observer1Packages.get(1));
+ assertEquals(APP_A, observer2Packages.get(0));
+ }
+
+ /**
+ * Test package failure and notifies only one observer because it handles the failure
+ */
+ @Test
+ public void testPackageFailureNotifyOne() throws Exception {
+ PackageWatchdog watchdog = createWatchdog();
+ TestObserver observer1 = new TestObserver(OBSERVER_NAME_1, true /* shouldHandle */);
+ TestObserver observer2 = new TestObserver(OBSERVER_NAME_2, true /* shouldHandle */);
+
+ // Start observing for observer1 and observer2 with failure handling
+ watchdog.startObservingHealth(observer2, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION);
+
+ // Then fail APP_A above the threshold
+ for (int i = 0; i < TRIGGER_FAILURE_COUNT; i++) {
+ watchdog.onPackageFailure(new String[]{APP_A});
+ }
+
+ // Verify only one observer is notifed
+ assertEquals(1, observer1.mFailedPackages.size());
+ assertEquals(APP_A, observer1.mFailedPackages.get(0));
+ assertEquals(0, observer2.mFailedPackages.size());
+ }
+
+ private PackageWatchdog createWatchdog() {
+ return new PackageWatchdog(InstrumentationRegistry.getContext(),
+ mTestLooper.getLooper());
+ }
+
+ private static class TestObserver implements PackageHealthObserver {
+ private final String mName;
+ private boolean mShouldHandle;
+ final List<String> mFailedPackages = new ArrayList<>();
+
+ TestObserver(String name) {
+ mName = name;
+ }
+
+ TestObserver(String name, boolean shouldHandle) {
+ mName = name;
+ mShouldHandle = shouldHandle;
+ }
+
+ public boolean onHealthCheckFailed(String packageName) {
+ mFailedPackages.add(packageName);
+ return mShouldHandle;
+ }
+
+ public String getName() {
+ return mName;
+ }
+ }
+}
diff --git a/tests/net/Android.mk b/tests/net/Android.mk
index 9d1edbf..f6f35fd 100644
--- a/tests/net/Android.mk
+++ b/tests/net/Android.mk
@@ -18,6 +18,7 @@
mockito-target-minus-junit4 \
platform-test-annotations \
services.core \
+ services.ipmemorystore \
services.net
LOCAL_JAVA_LIBRARIES := \
diff --git a/tests/net/java/android/net/IpMemoryStoreTest.java b/tests/net/java/android/net/IpMemoryStoreTest.java
new file mode 100644
index 0000000..eae9710
--- /dev/null
+++ b/tests/net/java/android/net/IpMemoryStoreTest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.content.Context;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class IpMemoryStoreTest {
+ @Mock
+ Context mMockContext;
+ @Mock
+ IIpMemoryStore mMockService;
+ IpMemoryStore mStore;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mStore = new IpMemoryStore(mMockContext, mMockService);
+ }
+
+ @Test
+ public void testNetworkAttributes() {
+ // TODO : implement this
+ }
+
+ @Test
+ public void testPrivateData() {
+ // TODO : implement this
+ }
+
+ @Test
+ public void testFindL2Key() {
+ // TODO : implement this
+ }
+
+ @Test
+ public void testIsSameNetwork() {
+ // TODO : implement this
+ }
+
+}
diff --git a/tests/net/java/android/net/ipmemorystore/ParcelableTests.java b/tests/net/java/android/net/ipmemorystore/ParcelableTests.java
new file mode 100644
index 0000000..a9f9758
--- /dev/null
+++ b/tests/net/java/android/net/ipmemorystore/ParcelableTests.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.ipmemorystore;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.util.Arrays;
+import java.util.Collections;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class ParcelableTests {
+ @Test
+ public void testNetworkAttributesParceling() throws Exception {
+ final NetworkAttributes.Builder builder = new NetworkAttributes.Builder();
+ NetworkAttributes in = builder.build();
+ assertEquals(in, new NetworkAttributes(parcelingRoundTrip(in.toParcelable())));
+
+ builder.setAssignedV4Address((Inet4Address) Inet4Address.getByName("1.2.3.4"));
+ // groupHint stays null this time around
+ builder.setDnsAddresses(Collections.emptyList());
+ builder.setMtu(18);
+ in = builder.build();
+ assertEquals(in, new NetworkAttributes(parcelingRoundTrip(in.toParcelable())));
+
+ builder.setAssignedV4Address((Inet4Address) Inet4Address.getByName("6.7.8.9"));
+ builder.setGroupHint("groupHint");
+ builder.setDnsAddresses(Arrays.asList(
+ InetAddress.getByName("ACA1:652B:0911:DE8F:1200:115E:913B:AA2A"),
+ InetAddress.getByName("6.7.8.9")));
+ builder.setMtu(1_000_000);
+ in = builder.build();
+ assertEquals(in, new NetworkAttributes(parcelingRoundTrip(in.toParcelable())));
+
+ builder.setMtu(null);
+ in = builder.build();
+ assertEquals(in, new NetworkAttributes(parcelingRoundTrip(in.toParcelable())));
+ }
+
+ @Test
+ public void testPrivateDataParceling() throws Exception {
+ final Blob in = new Blob();
+ in.data = new byte[] {89, 111, 108, 111};
+ final Blob out = parcelingRoundTrip(in);
+ // Object.equals on byte[] tests the references
+ assertEquals(in.data.length, out.data.length);
+ assertTrue(Arrays.equals(in.data, out.data));
+ }
+
+ @Test
+ public void testSameL3NetworkResponseParceling() throws Exception {
+ final SameL3NetworkResponseParcelable parcelable = new SameL3NetworkResponseParcelable();
+ parcelable.l2Key1 = "key 1";
+ parcelable.l2Key2 = "key 2";
+ parcelable.confidence = 0.43f;
+
+ final SameL3NetworkResponse in = new SameL3NetworkResponse(parcelable);
+ assertEquals("key 1", in.l2Key1);
+ assertEquals("key 2", in.l2Key2);
+ assertEquals(0.43f, in.confidence, 0.01f /* delta */);
+
+ final SameL3NetworkResponse out =
+ new SameL3NetworkResponse(parcelingRoundTrip(in.toParcelable()));
+
+ assertEquals(in, out);
+ assertEquals(in.l2Key1, out.l2Key1);
+ assertEquals(in.l2Key2, out.l2Key2);
+ assertEquals(in.confidence, out.confidence, 0.01f /* delta */);
+ }
+
+ private <T extends Parcelable> T parcelingRoundTrip(final T in) throws Exception {
+ final Parcel p = Parcel.obtain();
+ in.writeToParcel(p, /* flags */ 0);
+ p.setDataPosition(0);
+ final byte[] marshalledData = p.marshall();
+ p.recycle();
+
+ final Parcel q = Parcel.obtain();
+ q.unmarshall(marshalledData, 0, marshalledData.length);
+ q.setDataPosition(0);
+
+ final Parcelable.Creator<T> creator = (Parcelable.Creator<T>)
+ in.getClass().getField("CREATOR").get(null); // static object, so null receiver
+ final T unmarshalled = (T) creator.createFromParcel(q);
+ q.recycle();
+ return unmarshalled;
+ }
+}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 71529fd..bf39644 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -26,6 +26,8 @@
import static android.net.ConnectivityManager.TYPE_MOBILE_MMS;
import static android.net.ConnectivityManager.TYPE_NONE;
import static android.net.ConnectivityManager.TYPE_WIFI;
+import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_INVALID;
+import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_VALID;
import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS;
import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
@@ -69,17 +71,19 @@
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
-
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
@@ -89,7 +93,6 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
-import android.net.CaptivePortal;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
import android.net.ConnectivityManager.PacketKeepalive;
@@ -97,6 +100,8 @@
import android.net.ConnectivityManager.TooManyRequestsException;
import android.net.ConnectivityThread;
import android.net.INetd;
+import android.net.INetworkMonitor;
+import android.net.INetworkMonitorCallbacks;
import android.net.INetworkPolicyListener;
import android.net.INetworkPolicyManager;
import android.net.INetworkStatsService;
@@ -114,12 +119,14 @@
import android.net.NetworkMisc;
import android.net.NetworkRequest;
import android.net.NetworkSpecifier;
+import android.net.NetworkStack;
import android.net.NetworkUtils;
import android.net.RouteInfo;
import android.net.StringNetworkSpecifier;
import android.net.UidRange;
-import android.net.captiveportal.CaptivePortalProbeResult;
import android.net.metrics.IpConnectivityLog;
+import android.net.shared.NetworkMonitorUtils;
+import android.net.shared.PrivateDnsConfig;
import android.net.util.MultinetworkPolicyTracker;
import android.os.ConditionVariable;
import android.os.Handler;
@@ -148,12 +155,9 @@
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.server.connectivity.ConnectivityConstants;
import com.android.server.connectivity.DefaultNetworkMetrics;
-import com.android.server.connectivity.DnsManager;
import com.android.server.connectivity.IpConnectivityMetrics;
import com.android.server.connectivity.MockableSystemProperties;
import com.android.server.connectivity.Nat464Xlat;
-import com.android.server.connectivity.NetworkAgentInfo;
-import com.android.server.connectivity.NetworkMonitor;
import com.android.server.connectivity.Tethering;
import com.android.server.connectivity.Vpn;
import com.android.server.net.NetworkPinner;
@@ -168,6 +172,7 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
+import org.mockito.stubbing.Answer;
import java.net.Inet4Address;
import java.net.InetAddress;
@@ -230,6 +235,7 @@
@Mock INetworkStatsService mStatsService;
@Mock INetworkPolicyManager mNpm;
@Mock INetd mMockNetd;
+ @Mock NetworkStack mNetworkStack;
private ArgumentCaptor<String[]> mStringArrayCaptor = ArgumentCaptor.forClass(String[].class);
@@ -299,6 +305,7 @@
public Object getSystemService(String name) {
if (Context.CONNECTIVITY_SERVICE.equals(name)) return mCm;
if (Context.NOTIFICATION_SERVICE.equals(name)) return mock(NotificationManager.class);
+ if (Context.NETWORK_STACK_SERVICE.equals(name)) return mNetworkStack;
return super.getSystemService(name);
}
@@ -386,7 +393,7 @@
}
private class MockNetworkAgent {
- private final WrappedNetworkMonitor mWrappedNetworkMonitor;
+ private final INetworkMonitor mNetworkMonitor;
private final NetworkInfo mNetworkInfo;
private final NetworkCapabilities mNetworkCapabilities;
private final HandlerThread mHandlerThread;
@@ -402,6 +409,26 @@
// mNetworkStatusReceived.
private String mRedirectUrl;
+ private INetworkMonitorCallbacks mNmCallbacks;
+ private int mNmValidationResult = NETWORK_TEST_RESULT_INVALID;
+ private String mNmValidationRedirectUrl = null;
+ private boolean mNmProvNotificationRequested = false;
+
+ void setNetworkValid() {
+ mNmValidationResult = NETWORK_TEST_RESULT_VALID;
+ mNmValidationRedirectUrl = null;
+ }
+
+ void setNetworkInvalid() {
+ mNmValidationResult = NETWORK_TEST_RESULT_INVALID;
+ mNmValidationRedirectUrl = null;
+ }
+
+ void setNetworkPortal(String redirectUrl) {
+ setNetworkInvalid();
+ mNmValidationRedirectUrl = redirectUrl;
+ }
+
MockNetworkAgent(int transport) {
this(transport, new LinkProperties());
}
@@ -434,6 +461,29 @@
}
mHandlerThread = new HandlerThread("Mock-" + typeName);
mHandlerThread.start();
+
+ mNetworkMonitor = mock(INetworkMonitor.class);
+ final Answer validateAnswer = inv -> {
+ new Thread(this::onValidationRequested).start();
+ return null;
+ };
+
+ try {
+ doAnswer(validateAnswer).when(mNetworkMonitor).notifyNetworkConnected();
+ doAnswer(validateAnswer).when(mNetworkMonitor).forceReevaluation(anyInt());
+ } catch (RemoteException e) {
+ fail(e.getMessage());
+ }
+
+ final ArgumentCaptor<Network> nmNetworkCaptor =
+ ArgumentCaptor.forClass(Network.class);
+ final ArgumentCaptor<INetworkMonitorCallbacks> nmCbCaptor =
+ ArgumentCaptor.forClass(INetworkMonitorCallbacks.class);
+ doNothing().when(mNetworkStack).makeNetworkMonitor(
+ nmNetworkCaptor.capture(),
+ any() /* name */,
+ nmCbCaptor.capture());
+
mNetworkAgent = new NetworkAgent(mHandlerThread.getLooper(), mServiceContext,
"Mock-" + typeName, mNetworkInfo, mNetworkCapabilities,
linkProperties, mScore, new NetworkMisc()) {
@@ -465,10 +515,40 @@
mPreventReconnectReceived.open();
}
};
+
+ assertEquals(mNetworkAgent.netId, nmNetworkCaptor.getValue().netId);
+ mNmCallbacks = nmCbCaptor.getValue();
+
+ try {
+ mNmCallbacks.onNetworkMonitorCreated(mNetworkMonitor);
+ } catch (RemoteException e) {
+ fail(e.getMessage());
+ }
+
// Waits for the NetworkAgent to be registered, which includes the creation of the
// NetworkMonitor.
waitForIdle();
- mWrappedNetworkMonitor = mService.getLastCreatedWrappedNetworkMonitor();
+ }
+
+ private void onValidationRequested() {
+ try {
+ if (mNmProvNotificationRequested
+ && mNmValidationResult == NETWORK_TEST_RESULT_VALID) {
+ mNmCallbacks.hideProvisioningNotification();
+ mNmProvNotificationRequested = false;
+ }
+
+ mNmCallbacks.notifyNetworkTested(
+ mNmValidationResult, mNmValidationRedirectUrl);
+
+ if (mNmValidationRedirectUrl != null) {
+ mNmCallbacks.showProvisioningNotification(
+ "test_provisioning_notif_action");
+ mNmProvNotificationRequested = true;
+ }
+ } catch (RemoteException e) {
+ fail(e.getMessage());
+ }
}
public void adjustScore(int change) {
@@ -539,7 +619,7 @@
NetworkCallback callback = null;
final ConditionVariable validatedCv = new ConditionVariable();
if (validated) {
- mWrappedNetworkMonitor.gen204ProbeResult = 204;
+ setNetworkValid();
NetworkRequest request = new NetworkRequest.Builder()
.addTransportType(mNetworkCapabilities.getTransportTypes()[0])
.clearCapabilities()
@@ -564,15 +644,14 @@
if (validated) {
// Wait for network to validate.
waitFor(validatedCv);
- mWrappedNetworkMonitor.gen204ProbeResult = 500;
+ setNetworkInvalid();
}
if (callback != null) mCm.unregisterNetworkCallback(callback);
}
public void connectWithCaptivePortal(String redirectUrl) {
- mWrappedNetworkMonitor.gen204ProbeResult = 200;
- mWrappedNetworkMonitor.gen204ProbeRedirectUrl = redirectUrl;
+ setNetworkPortal(redirectUrl);
connect(false);
}
@@ -603,10 +682,6 @@
return mDisconnected;
}
- public WrappedNetworkMonitor getWrappedNetworkMonitor() {
- return mWrappedNetworkMonitor;
- }
-
public void sendLinkProperties(LinkProperties lp) {
mNetworkAgent.sendLinkProperties(lp);
}
@@ -880,28 +955,6 @@
}
}
- // NetworkMonitor implementation allowing overriding of Internet connectivity probe result.
- private class WrappedNetworkMonitor extends NetworkMonitor {
- public final Handler connectivityHandler;
- // HTTP response code fed back to NetworkMonitor for Internet connectivity probe.
- public int gen204ProbeResult = 500;
- public String gen204ProbeRedirectUrl = null;
-
- public WrappedNetworkMonitor(Context context, Handler handler,
- NetworkAgentInfo networkAgentInfo, NetworkRequest defaultRequest,
- IpConnectivityLog log) {
- super(context, handler, networkAgentInfo, defaultRequest, log,
- NetworkMonitor.Dependencies.DEFAULT);
- connectivityHandler = handler;
- }
-
- @Override
- protected CaptivePortalProbeResult isCaptivePortal() {
- if (!mIsCaptivePortalCheckEnabled) { return new CaptivePortalProbeResult(204); }
- return new CaptivePortalProbeResult(gen204ProbeResult, gen204ProbeRedirectUrl, null);
- }
- }
-
private class WrappedMultinetworkPolicyTracker extends MultinetworkPolicyTracker {
public volatile boolean configRestrictsAvoidBadWifi;
public volatile int configMeteredMultipathPreference;
@@ -923,7 +976,6 @@
private class WrappedConnectivityService extends ConnectivityService {
public WrappedMultinetworkPolicyTracker wrappedMultinetworkPolicyTracker;
- private WrappedNetworkMonitor mLastCreatedNetworkMonitor;
private MockableSystemProperties mSystemProperties;
public WrappedConnectivityService(Context context, INetworkManagementService netManager,
@@ -971,15 +1023,6 @@
}
}
- @Override
- public NetworkMonitor createNetworkMonitor(Context context, Handler handler,
- NetworkAgentInfo nai, NetworkRequest defaultRequest) {
- final WrappedNetworkMonitor monitor = new WrappedNetworkMonitor(
- context, handler, nai, defaultRequest, mock(IpConnectivityLog.class));
- mLastCreatedNetworkMonitor = monitor;
- return monitor;
- }
-
public Nat464Xlat getNat464Xlat(MockNetworkAgent mna) {
return getNetworkAgentInfoForNetwork(mna.getNetwork()).clatd;
}
@@ -1017,10 +1060,6 @@
protected void registerNetdEventCallback() {
}
- public WrappedNetworkMonitor getLastCreatedWrappedNetworkMonitor() {
- return mLastCreatedNetworkMonitor;
- }
-
public void mockVpn(int uid) {
synchronized (mVpns) {
int userId = UserHandle.getUserId(uid);
@@ -2439,7 +2478,7 @@
// Make captive portal disappear then revalidate.
// Expect onLost callback because network no longer provides NET_CAPABILITY_CAPTIVE_PORTAL.
- mWiFiNetworkAgent.getWrappedNetworkMonitor().gen204ProbeResult = 204;
+ mWiFiNetworkAgent.setNetworkValid();
mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), true);
captivePortalCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
@@ -2448,13 +2487,13 @@
// Break network connectivity.
// Expect NET_CAPABILITY_VALIDATED onLost callback.
- mWiFiNetworkAgent.getWrappedNetworkMonitor().gen204ProbeResult = 500;
+ mWiFiNetworkAgent.setNetworkInvalid();
mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), false);
validatedCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
}
@Test
- public void testCaptivePortalApp() {
+ public void testCaptivePortalApp() throws RemoteException {
final TestNetworkCallback captivePortalCallback = new TestNetworkCallback();
final NetworkRequest captivePortalRequest = new NetworkRequest.Builder()
.addCapability(NET_CAPABILITY_CAPTIVE_PORTAL).build();
@@ -2477,21 +2516,19 @@
mServiceContext.expectNoStartActivityIntent(fastTimeoutMs);
// Turn into a captive portal.
- mWiFiNetworkAgent.getWrappedNetworkMonitor().gen204ProbeResult = 302;
+ mWiFiNetworkAgent.setNetworkPortal("http://example.com");
mCm.reportNetworkConnectivity(wifiNetwork, false);
captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
validatedCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
- // Check that startCaptivePortalApp sends the expected intent.
+ // Check that startCaptivePortalApp sends the expected command to NetworkMonitor.
mCm.startCaptivePortalApp(wifiNetwork);
- Intent intent = mServiceContext.expectStartActivityIntent(TIMEOUT_MS);
- assertEquals(ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN, intent.getAction());
- assertEquals(wifiNetwork, intent.getExtra(ConnectivityManager.EXTRA_NETWORK));
+ verify(mWiFiNetworkAgent.mNetworkMonitor, timeout(TIMEOUT_MS).times(1))
+ .launchCaptivePortalApp();
- // Have the app report that the captive portal is dismissed, and check that we revalidate.
- mWiFiNetworkAgent.getWrappedNetworkMonitor().gen204ProbeResult = 204;
- CaptivePortal c = (CaptivePortal) intent.getExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL);
- c.reportCaptivePortalDismissed();
+ // Report that the captive portal is dismissed, and check that callbacks are fired
+ mWiFiNetworkAgent.setNetworkValid();
+ mWiFiNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid());
validatedCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
captivePortalCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
@@ -2524,20 +2561,6 @@
waitFor(avoidCv);
assertNoCallbacks(captivePortalCallback, validatedCallback);
-
- // Now test ignore mode.
- setCaptivePortalMode(Settings.Global.CAPTIVE_PORTAL_MODE_IGNORE);
-
- // Bring up a network with a captive portal.
- // Since we're ignoring captive portals, the network will validate.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
- String secondRedirectUrl = "http://example.com/secondPath";
- mWiFiNetworkAgent.connectWithCaptivePortal(secondRedirectUrl);
-
- // Expect NET_CAPABILITY_VALIDATED onAvailable callback.
- validatedCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
- // But there should be no CaptivePortal callback.
- captivePortalCallback.assertNoCallback();
}
private NetworkRequest.Builder newWifiRequestBuilder() {
@@ -3169,7 +3192,7 @@
Network wifiNetwork = mWiFiNetworkAgent.getNetwork();
// Fail validation on wifi.
- mWiFiNetworkAgent.getWrappedNetworkMonitor().gen204ProbeResult = 599;
+ mWiFiNetworkAgent.setNetworkInvalid();
mCm.reportNetworkConnectivity(wifiNetwork, false);
defaultCallback.expectCapabilitiesWithout(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
validatedWifiCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
@@ -3213,7 +3236,7 @@
wifiNetwork = mWiFiNetworkAgent.getNetwork();
// Fail validation on wifi and expect the dialog to appear.
- mWiFiNetworkAgent.getWrappedNetworkMonitor().gen204ProbeResult = 599;
+ mWiFiNetworkAgent.setNetworkInvalid();
mCm.reportNetworkConnectivity(wifiNetwork, false);
defaultCallback.expectCapabilitiesWithout(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
validatedWifiCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
@@ -4002,11 +4025,9 @@
final String TLS_SERVER6 = "2001:db8:53::53";
final InetAddress[] TLS_IPS = new InetAddress[]{ InetAddress.getByName(TLS_SERVER6) };
final String[] TLS_SERVERS = new String[]{ TLS_SERVER6 };
- final Handler h = mCellNetworkAgent.getWrappedNetworkMonitor().connectivityHandler;
- h.sendMessage(h.obtainMessage(
- NetworkMonitor.EVENT_PRIVATE_DNS_CONFIG_RESOLVED, 0,
- mCellNetworkAgent.getNetwork().netId,
- new DnsManager.PrivateDnsConfig(TLS_SPECIFIER, TLS_IPS)));
+ mCellNetworkAgent.mNmCallbacks.notifyPrivateDnsConfigResolved(
+ new PrivateDnsConfig(TLS_SPECIFIER, TLS_IPS).toParcel());
+
waitForIdle();
verify(mNetworkManagementService, atLeastOnce()).setDnsConfigurationForNetwork(
anyInt(), mStringArrayCaptor.capture(), any(), any(),
@@ -4294,6 +4315,12 @@
ranges.add(new UidRange(uid, uid));
mMockVpn.setNetworkAgent(vpnNetworkAgent);
mMockVpn.setUids(ranges);
+ // VPN networks do not satisfy the default request and are automatically validated
+ // by NetworkMonitor
+ assertFalse(NetworkMonitorUtils.isValidationRequired(
+ mCm.getDefaultRequest().networkCapabilities, vpnNetworkAgent.mNetworkCapabilities));
+ vpnNetworkAgent.setNetworkValid();
+
vpnNetworkAgent.connect(false);
mMockVpn.connect();
diff --git a/tests/net/java/com/android/server/connectivity/DnsManagerTest.java b/tests/net/java/com/android/server/connectivity/DnsManagerTest.java
index 01b468a..38322e9 100644
--- a/tests/net/java/com/android/server/connectivity/DnsManagerTest.java
+++ b/tests/net/java/com/android/server/connectivity/DnsManagerTest.java
@@ -17,7 +17,6 @@
package com.android.server.connectivity;
import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF;
-import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
import static android.provider.Settings.Global.PRIVATE_DNS_DEFAULT_MODE;
import static android.provider.Settings.Global.PRIVATE_DNS_MODE;
@@ -29,13 +28,13 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.when;
-import android.content.ContentResolver;
import android.content.Context;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.Network;
import android.net.RouteInfo;
+import android.net.shared.PrivateDnsConfig;
import android.os.INetworkManagementService;
import android.provider.Settings;
import android.support.test.filters.SmallTest;
@@ -43,18 +42,16 @@
import android.test.mock.MockContentResolver;
import com.android.internal.util.test.FakeSettingsProvider;
-import com.android.server.connectivity.DnsManager.PrivateDnsConfig;
-import com.android.server.connectivity.MockableSystemProperties;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
import java.net.InetAddress;
import java.util.Arrays;
-import org.junit.runner.RunWith;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
/**
* Tests for {@link DnsManager}.
*
@@ -133,7 +130,7 @@
PRIVATE_DNS_MODE, PRIVATE_DNS_MODE_PROVIDER_HOSTNAME);
Settings.Global.putString(mContentResolver, PRIVATE_DNS_SPECIFIER, "strictmode.com");
mDnsManager.updatePrivateDns(new Network(TEST_NETID),
- new DnsManager.PrivateDnsConfig("strictmode.com", new InetAddress[] {
+ new PrivateDnsConfig("strictmode.com", new InetAddress[] {
InetAddress.parseNumericAddress("6.6.6.6"),
InetAddress.parseNumericAddress("2001:db8:66:66::1")
}));
diff --git a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
index 354cf2f..4c52d81 100644
--- a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
@@ -23,10 +23,10 @@
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import static org.mockito.Mockito.reset;
import android.app.PendingIntent;
import android.content.Context;
@@ -36,18 +36,18 @@
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.NetworkMisc;
-import android.support.test.runner.AndroidJUnit4;
+import android.net.NetworkStack;
import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
import android.text.format.DateUtils;
import com.android.internal.R;
import com.android.server.ConnectivityService;
-import com.android.server.connectivity.NetworkNotificationManager;
import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
-import org.junit.runner.RunWith;
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -70,13 +70,16 @@
@Mock NetworkMisc mMisc;
@Mock NetworkNotificationManager mNotifier;
@Mock Resources mResources;
+ @Mock NetworkStack mNetworkStack;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
when(mCtx.getResources()).thenReturn(mResources);
when(mCtx.getPackageName()).thenReturn("com.android.server.connectivity");
- when(mConnService.createNetworkMonitor(any(), any(), any(), any())).thenReturn(null);
+ when(mCtx.getSystemServiceName(NetworkStack.class))
+ .thenReturn(Context.NETWORK_STACK_SERVICE);
+ when(mCtx.getSystemService(Context.NETWORK_STACK_SERVICE)).thenReturn(mNetworkStack);
mMonitor = new TestableLingerMonitor(mCtx, mNotifier, HIGH_DAILY_LIMIT, HIGH_RATE_LIMIT);
}
@@ -349,7 +352,7 @@
caps.addCapability(0);
caps.addTransportType(transport);
NetworkAgentInfo nai = new NetworkAgentInfo(null, null, new Network(netId), info, null,
- caps, 50, mCtx, null, mMisc, null, mConnService);
+ caps, 50, mCtx, null, mMisc, mConnService);
nai.everValidated = true;
return nai;
}
diff --git a/tests/net/java/com/android/server/net/ipmemorystore/IpMemoryStoreServiceTest.java b/tests/net/java/com/android/server/net/ipmemorystore/IpMemoryStoreServiceTest.java
new file mode 100644
index 0000000..859a54d
--- /dev/null
+++ b/tests/net/java/com/android/server/net/ipmemorystore/IpMemoryStoreServiceTest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.net.ipmemorystore;
+
+import android.content.Context;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/** Unit tests for {@link IpMemoryStoreServiceTest}. */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class IpMemoryStoreServiceTest {
+ @Mock
+ Context mMockContext;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void testNetworkAttributes() {
+ final IpMemoryStoreService service = new IpMemoryStoreService(mMockContext);
+ // TODO : implement this
+ }
+
+ @Test
+ public void testPrivateData() {
+ final IpMemoryStoreService service = new IpMemoryStoreService(mMockContext);
+ // TODO : implement this
+ }
+
+ @Test
+ public void testFindL2Key() {
+ final IpMemoryStoreService service = new IpMemoryStoreService(mMockContext);
+ // TODO : implement this
+ }
+
+ @Test
+ public void testIsSameNetwork() {
+ final IpMemoryStoreService service = new IpMemoryStoreService(mMockContext);
+ // TODO : implement this
+ }
+}
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
index 9968bda..4491a85 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -67,7 +67,7 @@
case JAVA_TYPE_STRING:
return "char const*";
case JAVA_TYPE_BYTE_ARRAY:
- return "char const*";
+ return "const BytesField&";
default:
return "UNKNOWN";
}
@@ -270,10 +270,6 @@
chainField.name.c_str(), chainField.name.c_str());
}
}
- } else if (*arg == JAVA_TYPE_BYTE_ARRAY) {
- fprintf(out, ", %s arg%d, size_t arg%d_length",
- cpp_type_name(*arg), argIndex, argIndex);
-
} else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
fprintf(out, ", const std::map<int, int32_t>& arg%d_1, "
"const std::map<int, int64_t>& arg%d_2, "
@@ -355,7 +351,8 @@
fprintf(out, " event.end();\n\n");
} else if (*arg == JAVA_TYPE_BYTE_ARRAY) {
fprintf(out,
- " event.AppendCharArray(arg%d, arg%d_length);\n",
+ " event.AppendCharArray(arg%d.arg, "
+ "arg%d.arg_length);\n",
argIndex, argIndex);
} else {
if (*arg == JAVA_TYPE_STRING) {
@@ -397,10 +394,6 @@
chainField.name.c_str(), chainField.name.c_str());
}
}
- } else if (*arg == JAVA_TYPE_BYTE_ARRAY) {
- fprintf(out, ", %s arg%d, size_t arg%d_length",
- cpp_type_name(*arg), argIndex, argIndex);
-
} else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
fprintf(out,
", const std::map<int, int32_t>& arg%d_1, "
@@ -434,8 +427,6 @@
chainField.name.c_str(), chainField.name.c_str());
}
}
- } else if (*arg == JAVA_TYPE_BYTE_ARRAY) {
- fprintf(out, ", arg%d, arg%d_length", argIndex, argIndex);
} else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
fprintf(out, ", arg%d_1, arg%d_2, arg%d_3, arg%d_4", argIndex,
argIndex, argIndex, argIndex);
@@ -494,7 +485,14 @@
fprintf(out, " arg%d = \"\";\n", argIndex);
fprintf(out, " }\n");
}
- fprintf(out, " event << arg%d;\n", argIndex);
+ if (*arg == JAVA_TYPE_BYTE_ARRAY) {
+ fprintf(out,
+ " event.AppendCharArray(arg%d.arg, "
+ "arg%d.arg_length);",
+ argIndex, argIndex);
+ } else {
+ fprintf(out, " event << arg%d;\n", argIndex);
+ }
if (argIndex == 2) {
fprintf(out, " event.end();\n\n");
fprintf(out, " event.end();\n\n");
@@ -577,7 +575,9 @@
static void write_cpp_usage(
FILE* out, const string& method_name, const string& atom_code_name,
const AtomDecl& atom, const AtomDecl &attributionDecl) {
- fprintf(out, " * Usage: %s(StatsLog.%s", method_name.c_str(), atom_code_name.c_str());
+ fprintf(out, " * Usage: %s(StatsLog.%s", method_name.c_str(),
+ atom_code_name.c_str());
+
for (vector<AtomField>::const_iterator field = atom.fields.begin();
field != atom.fields.end(); field++) {
if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
@@ -601,11 +601,6 @@
field->name.c_str(),
field->name.c_str(),
field->name.c_str());
- } else if (field->javaType == JAVA_TYPE_BYTE_ARRAY) {
- fprintf(out, ", %s %s, size_t %s_length",
- cpp_type_name(field->javaType), field->name.c_str(),
- field->name.c_str());
-
} else {
fprintf(out, ", %s %s", cpp_type_name(field->javaType), field->name.c_str());
}
@@ -639,9 +634,6 @@
"const std::map<int, char const*>& arg%d_3, "
"const std::map<int, float>& arg%d_4",
argIndex, argIndex, argIndex, argIndex);
- } else if (*arg == JAVA_TYPE_BYTE_ARRAY) {
- fprintf(out, ", %s arg%d, size_t arg%d_length",
- cpp_type_name(*arg), argIndex, argIndex);
} else {
fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
}
@@ -708,6 +700,15 @@
fprintf(out, "};\n");
fprintf(out, "\n");
+ fprintf(out, "struct BytesField {\n");
+ fprintf(out,
+ " BytesField(char const* array, size_t len) : arg(array), "
+ "arg_length(len) {}\n");
+ fprintf(out, " char const* arg;\n");
+ fprintf(out, " size_t arg_length;\n");
+ fprintf(out, "};\n");
+ fprintf(out, "\n");
+
fprintf(out, "struct StateAtomFieldOptions {\n");
fprintf(out, " std::vector<int> primaryFields;\n");
fprintf(out, " int exclusiveField;\n");
@@ -1183,6 +1184,11 @@
fprintf(out, " str%d = NULL;\n", argIndex);
fprintf(out, " }\n");
+ fprintf(out,
+ " android::util::BytesField bytesField%d(str%d, "
+ "str%d_length);",
+ argIndex, argIndex, argIndex);
+
} else if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
hadStringOrChain = true;
for (auto chainField : attributionDecl.fields) {
@@ -1240,7 +1246,8 @@
// stats_write call
argIndex = 1;
- fprintf(out, "\n int ret = android::util::%s(code", cpp_method_name.c_str());
+ fprintf(out, "\n int ret = android::util::%s(code",
+ cpp_method_name.c_str());
for (vector<java_type_t>::const_iterator arg = signature->begin();
arg != signature->end(); arg++) {
if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
@@ -1255,16 +1262,12 @@
}
} else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
fprintf(out, ", int32_t_map, int64_t_map, string_map, float_map");
+ } else if (*arg == JAVA_TYPE_BYTE_ARRAY) {
+ fprintf(out, ", bytesField%d", argIndex);
} else {
- const char* argName = (*arg == JAVA_TYPE_STRING ||
- *arg == JAVA_TYPE_BYTE_ARRAY)
- ? "str"
- : "arg";
+ const char* argName =
+ (*arg == JAVA_TYPE_STRING) ? "str" : "arg";
fprintf(out, ", (%s)%s%d", cpp_type_name(*arg), argName, argIndex);
-
- if (*arg == JAVA_TYPE_BYTE_ARRAY) {
- fprintf(out, ", %s%d_length", argName, argIndex);
- }
}
argIndex++;
}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 7f56a01..517bf3b 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -189,6 +189,7 @@
*/
public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_REMOVE_INVALID = 5;
+ /** @hide */
@IntDef(prefix = { "STATUS_NETWORK_SUGGESTIONS_" }, value = {
STATUS_NETWORK_SUGGESTIONS_SUCCESS,
STATUS_NETWORK_SUGGESTIONS_ERROR_INTERNAL,
@@ -1652,7 +1653,7 @@
* suggestion back using this API.</li>
*
* @param networkSuggestions List of network suggestions provided by the app.
- * @return Status code corresponding to the values in {@link NetworkSuggestionsStatusCode}.
+ * @return Status code for the operation. One of the STATUS_NETWORK_SUGGESTIONS_ values.
* {@link WifiNetworkSuggestion#equals(Object)} any previously provided suggestions by the app.
* @throws {@link SecurityException} if the caller is missing required permissions.
*/
@@ -1673,8 +1674,7 @@
*
* @param networkSuggestions List of network suggestions to be removed. Pass an empty list
* to remove all the previous suggestions provided by the app.
- * @return Status code corresponding to the values in
- * {@link NetworkSuggestionsStatusCode}.
+ * @return Status code for the operation. One of the STATUS_NETWORK_SUGGESTIONS_ values.
* Any matching suggestions are removed from the device and will not be considered for any
* further connection attempts.
*/