Merge "Defined new APIs to whitelist content capture for specific URLs."
diff --git a/Android.bp b/Android.bp
index a6745a9..8a3f76c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -318,6 +318,7 @@
"core/java/android/service/vr/IVrListener.aidl",
"core/java/android/service/vr/IVrManager.aidl",
"core/java/android/service/vr/IVrStateCallbacks.aidl",
+ "core/java/android/service/watchdog/IExplicitHealthCheckService.aidl",
"core/java/android/print/ILayoutResultCallback.aidl",
"core/java/android/print/IPrinterDiscoveryObserver.aidl",
"core/java/android/print/IPrintDocumentAdapter.aidl",
@@ -475,7 +476,10 @@
"media/java/android/media/IMediaHTTPConnection.aidl",
"media/java/android/media/IMediaHTTPService.aidl",
"media/java/android/media/IMediaResourceMonitor.aidl",
+ "media/java/android/media/IMediaRoute2Callback.aidl",
+ "media/java/android/media/IMediaRoute2Provider.aidl",
"media/java/android/media/IMediaRouterClient.aidl",
+ "media/java/android/media/IMediaRouter2ManagerClient.aidl",
"media/java/android/media/IMediaRouterService.aidl",
"media/java/android/media/IMediaScannerListener.aidl",
"media/java/android/media/IMediaScannerService.aidl",
diff --git a/apct-tests/perftests/core/src/android/os/KernelCpuThreadReaderPerfTest.java b/apct-tests/perftests/core/src/android/os/KernelCpuThreadReaderPerfTest.java
index 0c30302..1f26188 100644
--- a/apct-tests/perftests/core/src/android/os/KernelCpuThreadReaderPerfTest.java
+++ b/apct-tests/perftests/core/src/android/os/KernelCpuThreadReaderPerfTest.java
@@ -47,7 +47,7 @@
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
assertNotNull(mKernelCpuThreadReader);
while (state.keepRunning()) {
- this.mKernelCpuThreadReader.getCurrentProcessCpuUsage();
+ this.mKernelCpuThreadReader.getProcessCpuUsage();
}
}
}
diff --git a/api/current.txt b/api/current.txt
index 9d59ab3..2a26368 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6963,7 +6963,7 @@
public abstract static class DevicePolicyManager.InstallSystemUpdateCallback {
ctor public DevicePolicyManager.InstallSystemUpdateCallback();
- method public void onInstallUpdateError(int, String);
+ method public void onInstallUpdateError(int, @NonNull String);
field public static final int UPDATE_ERROR_BATTERY_LOW = 5; // 0x5
field public static final int UPDATE_ERROR_FILE_NOT_FOUND = 4; // 0x4
field public static final int UPDATE_ERROR_INCORRECT_OS_VERSION = 2; // 0x2
@@ -11384,6 +11384,7 @@
public class PackageInstaller {
method public void abandonSession(int);
method public int createSession(@NonNull android.content.pm.PackageInstaller.SessionParams) throws java.io.IOException;
+ method @Nullable public android.content.pm.PackageInstaller.SessionInfo getActiveStagedSession();
method @NonNull public java.util.List<android.content.pm.PackageInstaller.SessionInfo> getAllSessions();
method @NonNull public java.util.List<android.content.pm.PackageInstaller.SessionInfo> getMySessions();
method @Nullable public android.content.pm.PackageInstaller.SessionInfo getSessionInfo(int);
@@ -11468,6 +11469,7 @@
method @NonNull public String getStagedSessionErrorMessage();
method @NonNull public android.os.UserHandle getUser();
method public boolean isActive();
+ method public boolean isCommitted();
method public boolean isMultiPackage();
method public boolean isSealed();
method public boolean isStaged();
@@ -13087,10 +13089,10 @@
method @Deprecated public String buildUnionSubQuery(String, String[], java.util.Set<java.lang.String>, int, String, String, String[], String, String);
method public int delete(@NonNull android.database.sqlite.SQLiteDatabase, @Nullable String, @Nullable String[]);
method @Nullable public android.database.sqlite.SQLiteDatabase.CursorFactory getCursorFactory();
- method public boolean getDistinct();
method @Nullable public java.util.Map<java.lang.String,java.lang.String> getProjectionMap();
- method public boolean getStrict();
method @Nullable public String getTables();
+ method public boolean isDistinct();
+ method public boolean isStrict();
method public android.database.Cursor query(android.database.sqlite.SQLiteDatabase, String[], String, String[], String, String, String);
method public android.database.Cursor query(android.database.sqlite.SQLiteDatabase, String[], String, String[], String, String, String, String);
method public android.database.Cursor query(android.database.sqlite.SQLiteDatabase, String[], String, String[], String, String, String, String, android.os.CancellationSignal);
@@ -29265,7 +29267,6 @@
method public abstract boolean isRelative();
method public android.net.Uri normalizeScheme();
method public static android.net.Uri parse(String);
- method @NonNull public String toSafeString();
method public abstract String toString();
method public static android.net.Uri withAppendedPath(android.net.Uri, String);
method public static void writeToParcel(android.os.Parcel, android.net.Uri);
diff --git a/api/system-current.txt b/api/system-current.txt
index 3d403607..23cecf4 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1740,7 +1740,7 @@
}
public class ShortcutManager {
- method @NonNull public java.util.List<android.content.pm.ShortcutManager.ShareShortcutInfo> getShareTargets(@NonNull android.content.IntentFilter);
+ method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_APP_PREDICTIONS) public java.util.List<android.content.pm.ShortcutManager.ShareShortcutInfo> getShareTargets(@NonNull android.content.IntentFilter);
method public boolean hasShareTargets(@NonNull String);
}
@@ -4233,6 +4233,10 @@
field public static final int TAG_SYSTEM_PROBE = -190; // 0xffffff42
}
+ public abstract class Uri implements java.lang.Comparable<android.net.Uri> android.os.Parcelable {
+ method @NonNull public String toSafeString();
+ }
+
public class VpnService extends android.app.Service {
method @RequiresPermission(android.Manifest.permission.CONTROL_VPN) public static void prepareAndAuthorize(android.content.Context);
}
@@ -5664,7 +5668,7 @@
}
public static interface DynamicSystemClient.OnStatusChangedListener {
- method public void onStatusChanged(int, int, long);
+ method public void onStatusChanged(int, int, long, @Nullable Throwable);
}
}
@@ -6896,6 +6900,22 @@
}
+package android.service.watchdog {
+
+ public abstract class ExplicitHealthCheckService extends android.app.Service {
+ ctor public ExplicitHealthCheckService();
+ method public final void notifyHealthCheckPassed(@NonNull String);
+ method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
+ method public abstract void onCancelHealthCheck(@NonNull String);
+ method @NonNull public abstract java.util.List<java.lang.String> onGetRequestedPackages();
+ method @NonNull public abstract java.util.List<java.lang.String> onGetSupportedPackages();
+ method public abstract void onRequestHealthCheck(@NonNull String);
+ field public static final String BIND_PERMISSION = "android.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE";
+ field public static final String SERVICE_INTERFACE = "android.service.watchdog.ExplicitHealthCheckService";
+ }
+
+}
+
package android.telecom {
@Deprecated public class AudioState implements android.os.Parcelable {
diff --git a/api/test-current.txt b/api/test-current.txt
index f186e238..6c0b928 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -2150,9 +2150,9 @@
method @RequiresPermission(android.Manifest.permission.CLEAR_APP_USER_DATA) public static long getContributedMediaSize(android.content.Context, String, android.os.UserHandle) throws java.io.IOException;
method @NonNull public static java.io.File getVolumePath(@NonNull String) throws java.io.FileNotFoundException;
method @NonNull public static java.util.Collection<java.io.File> getVolumeScanPaths(@NonNull String) throws java.io.FileNotFoundException;
- field public static final String EXTRA_ORIGINATED_FROM_SHELL = "android.intent.extra.originated_from_shell";
- field public static final String SCAN_FILE_CALL = "scan_file";
- field public static final String SCAN_VOLUME_CALL = "scan_volume";
+ method public static android.net.Uri scanFile(android.content.Context, java.io.File);
+ method public static android.net.Uri scanFileFromShell(android.content.Context, java.io.File);
+ method public static void scanVolume(android.content.Context, java.io.File);
}
public final class Settings {
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index a6c7cae..8e7277c 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -101,7 +101,7 @@
BootAnimation::BootAnimation(sp<Callbacks> callbacks)
: Thread(false), mClockEnabled(true), mTimeIsAccurate(false),
- mTimeFormat12Hour(false), mTimeCheckThread(NULL), mCallbacks(callbacks) {
+ mTimeFormat12Hour(false), mTimeCheckThread(nullptr), mCallbacks(callbacks) {
mSession = new SurfaceComposerClient();
std::string powerCtl = android::base::GetProperty("sys.powerctl", "");
@@ -156,7 +156,7 @@
status_t BootAnimation::initTexture(Texture* texture, AssetManager& assets,
const char* name) {
Asset* asset = assets.open(name, Asset::ACCESS_BUFFER);
- if (asset == NULL)
+ if (asset == nullptr)
return NO_INIT;
SkBitmap bitmap;
sk_sp<SkData> data = SkData::MakeWithoutCopy(asset->getBuffer(false),
@@ -234,7 +234,7 @@
case kN32_SkColorType:
if (!mUseNpotTextures && (tw != w || th != h)) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tw, th, 0, GL_RGBA,
- GL_UNSIGNED_BYTE, 0);
+ GL_UNSIGNED_BYTE, nullptr);
glTexSubImage2D(GL_TEXTURE_2D, 0,
0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, p);
} else {
@@ -246,7 +246,7 @@
case kRGB_565_SkColorType:
if (!mUseNpotTextures && (tw != w || th != h)) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, tw, th, 0, GL_RGB,
- GL_UNSIGNED_SHORT_5_6_5, 0);
+ GL_UNSIGNED_SHORT_5_6_5, nullptr);
glTexSubImage2D(GL_TEXTURE_2D, 0,
0, 0, w, h, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, p);
} else {
@@ -304,10 +304,10 @@
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
- eglInitialize(display, 0, 0);
+ eglInitialize(display, nullptr, nullptr);
eglChooseConfig(display, attribs, &config, 1, &numConfigs);
- surface = eglCreateWindowSurface(display, config, s.get(), NULL);
- context = eglCreateContext(display, config, NULL, NULL);
+ surface = eglCreateWindowSurface(display, config, s.get(), nullptr);
+ context = eglCreateContext(display, config, nullptr, nullptr);
eglQuerySurface(display, surface, EGL_WIDTH, &w);
eglQuerySurface(display, surface, EGL_HEIGHT, &h);
@@ -671,7 +671,7 @@
// Parse the description file
for (;;) {
const char* endl = strstr(s, "\n");
- if (endl == NULL) break;
+ if (endl == nullptr) break;
String8 line(s, endl - s);
const char* l = line.string();
int fps = 0;
@@ -699,8 +699,8 @@
part.count = count;
part.pause = pause;
part.path = path;
- part.audioData = NULL;
- part.animation = NULL;
+ part.audioData = nullptr;
+ part.animation = nullptr;
if (!parseColor(color, part.backgroundColor)) {
SLOGE("> invalid color '#%s'", color);
part.backgroundColor[0] = 0.0f;
@@ -716,9 +716,9 @@
part.playUntilComplete = false;
part.count = 1;
part.pause = 0;
- part.audioData = NULL;
+ part.audioData = nullptr;
part.animation = loadAnimation(String8(SYSTEM_BOOTANIMATION_FILE));
- if (part.animation != NULL)
+ if (part.animation != nullptr)
animation.parts.add(part);
}
s = ++endl;
@@ -731,7 +731,7 @@
{
// read all the data structures
const size_t pcount = animation.parts.size();
- void *cookie = NULL;
+ void *cookie = nullptr;
ZipFileRO* zip = animation.zip;
if (!zip->startIteration(&cookie)) {
return false;
@@ -739,7 +739,7 @@
ZipEntryRO entry;
char name[ANIM_ENTRY_NAME_MAX];
- while ((entry = zip->nextEntry(cookie)) != NULL) {
+ while ((entry = zip->nextEntry(cookie)) != nullptr) {
const int foundEntryName = zip->getEntryFileName(entry, name, ANIM_ENTRY_NAME_MAX);
if (foundEntryName > ANIM_ENTRY_NAME_MAX || foundEntryName == -1) {
SLOGE("Error fetching entry file name");
@@ -762,7 +762,7 @@
if (path == animation.parts[j].path) {
uint16_t method;
// supports only stored png files
- if (zip->getEntryInfo(entry, &method, NULL, NULL, NULL, NULL, NULL)) {
+ if (zip->getEntryInfo(entry, &method, nullptr, nullptr, nullptr, nullptr, nullptr)) {
if (method == ZipFileRO::kCompressStored) {
FileMap* map = zip->createEntryFileMap(entry);
if (map) {
@@ -800,7 +800,7 @@
for (size_t frameIdx = 0; frameIdx < part.frames.size(); frameIdx++) {
const char* endl = strstr(trimDataStr, "\n");
// No more trimData for this part.
- if (endl == NULL) {
+ if (endl == nullptr) {
break;
}
String8 line(trimDataStr, endl - trimDataStr);
@@ -927,7 +927,7 @@
glBindTexture(GL_TEXTURE_2D, 0);
// Handle animation package
- if (part.animation != NULL) {
+ if (part.animation != nullptr) {
playAnimation(*part.animation);
if (exitPending())
break;
@@ -1001,7 +1001,7 @@
spec.tv_nsec = (now + delay) % 1000000000;
int err;
do {
- err = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &spec, NULL);
+ err = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &spec, nullptr);
} while (err<0 && errno == EINTR);
}
@@ -1090,13 +1090,13 @@
if (mLoadedFiles.indexOf(fn) >= 0) {
SLOGE("File \"%s\" is already loaded. Cyclic ref is not allowed",
fn.string());
- return NULL;
+ return nullptr;
}
ZipFileRO *zip = ZipFileRO::open(fn);
- if (zip == NULL) {
+ if (zip == nullptr) {
SLOGE("Failed to open animation zip \"%s\": %s",
fn.string(), strerror(errno));
- return NULL;
+ return nullptr;
}
Animation *animation = new Animation;
@@ -1107,7 +1107,7 @@
parseAnimationDesc(*animation);
if (!preloadZip(*animation)) {
- return NULL;
+ return nullptr;
}
@@ -1135,7 +1135,7 @@
}
FILE* file = fopen(LAST_TIME_CHANGED_FILE_PATH, "r");
- if (file != NULL) {
+ if (file != nullptr) {
long long lastChangedTime = 0;
fscanf(file, "%lld", &lastChangedTime);
fclose(file);
diff --git a/cmds/bootanimation/audioplay.cpp b/cmds/bootanimation/audioplay.cpp
index 874aab0..c5e16c6 100644
--- a/cmds/bootanimation/audioplay.cpp
+++ b/cmds/bootanimation/audioplay.cpp
@@ -39,14 +39,14 @@
using namespace android;
// engine interfaces
-static SLObjectItf engineObject = NULL;
+static SLObjectItf engineObject = nullptr;
static SLEngineItf engineEngine;
// output mix interfaces
-static SLObjectItf outputMixObject = NULL;
+static SLObjectItf outputMixObject = nullptr;
// buffer queue player interfaces
-static SLObjectItf bqPlayerObject = NULL;
+static SLObjectItf bqPlayerObject = nullptr;
static SLPlayItf bqPlayerPlay;
static SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue;
static SLMuteSoloItf bqPlayerMuteSolo;
@@ -89,7 +89,7 @@
}
bool hasPlayer() {
- return (engineObject != NULL && bqPlayerObject != NULL);
+ return (engineObject != nullptr && bqPlayerObject != nullptr);
}
// create the engine and output mix objects
@@ -97,7 +97,7 @@
SLresult result;
// create engine
- result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
+ result = slCreateEngine(&engineObject, 0, nullptr, 0, nullptr, nullptr);
if (result != SL_RESULT_SUCCESS) {
ALOGE("slCreateEngine failed with result %d", result);
return false;
@@ -121,7 +121,7 @@
(void)result;
// create output mix
- result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, NULL, NULL);
+ result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, nullptr, nullptr);
if (result != SL_RESULT_SUCCESS) {
ALOGE("sl engine CreateOutputMix failed with result %d", result);
return false;
@@ -173,7 +173,7 @@
// configure audio sink
SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject};
- SLDataSink audioSnk = {&loc_outmix, NULL};
+ SLDataSink audioSnk = {&loc_outmix, nullptr};
// create audio player
const SLInterfaceID ids[3] = {SL_IID_BUFFERQUEUE, SL_IID_VOLUME, SL_IID_ANDROIDCONFIGURATION};
@@ -236,7 +236,7 @@
(void)result;
// register callback on the buffer queue
- result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, NULL);
+ result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, nullptr);
if (result != SL_RESULT_SUCCESS) {
ALOGE("sl bqPlayerBufferQueue RegisterCallback failed with result %d", result);
return false;
@@ -261,7 +261,7 @@
const uint8_t** oSoundBuf, unsigned* oSoundBufSize) {
*oSoundBuf = clipBuf;
*oSoundBufSize = clipBufSize;
- *oChunkFormat = NULL;
+ *oChunkFormat = nullptr;
const RiffWaveHeader* wavHeader = (const RiffWaveHeader*)*oSoundBuf;
if (*oSoundBufSize < sizeof(*wavHeader) || (wavHeader->riff_id != ID_RIFF) ||
(wavHeader->wave_id != ID_WAVE)) {
@@ -303,7 +303,7 @@
}
}
- if (*oChunkFormat == NULL) {
+ if (*oChunkFormat == nullptr) {
ALOGE("format not found in WAV file");
return false;
}
@@ -435,7 +435,7 @@
SLresult result;
- if (NULL != bqPlayerPlay) {
+ if (nullptr != bqPlayerPlay) {
// set the player's state
result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay,
isPlaying ? SL_PLAYSTATE_PLAYING : SL_PLAYSTATE_STOPPED);
@@ -445,28 +445,28 @@
void destroy() {
// destroy buffer queue audio player object, and invalidate all associated interfaces
- if (bqPlayerObject != NULL) {
+ if (bqPlayerObject != nullptr) {
CHATTY("destroying audio player");
(*bqPlayerObject)->Destroy(bqPlayerObject);
- bqPlayerObject = NULL;
- bqPlayerPlay = NULL;
- bqPlayerBufferQueue = NULL;
- bqPlayerMuteSolo = NULL;
- bqPlayerVolume = NULL;
+ bqPlayerObject = nullptr;
+ bqPlayerPlay = nullptr;
+ bqPlayerBufferQueue = nullptr;
+ bqPlayerMuteSolo = nullptr;
+ bqPlayerVolume = nullptr;
}
// destroy output mix object, and invalidate all associated interfaces
- if (outputMixObject != NULL) {
+ if (outputMixObject != nullptr) {
(*outputMixObject)->Destroy(outputMixObject);
- outputMixObject = NULL;
+ outputMixObject = nullptr;
}
// destroy engine object, and invalidate all associated interfaces
- if (engineObject != NULL) {
+ if (engineObject != nullptr) {
CHATTY("destroying audio engine");
(*engineObject)->Destroy(engineObject);
- engineObject = NULL;
- engineEngine = NULL;
+ engineObject = nullptr;
+ engineEngine = nullptr;
}
}
diff --git a/cmds/bootanimation/iot/iotbootanimation_main.cpp b/cmds/bootanimation/iot/iotbootanimation_main.cpp
index 2a3d376..ae35297 100644
--- a/cmds/bootanimation/iot/iotbootanimation_main.cpp
+++ b/cmds/bootanimation/iot/iotbootanimation_main.cpp
@@ -60,7 +60,7 @@
mBootAction = new BootAction();
if (!mBootAction->init(library_path, mBootParameters)) {
- mBootAction = NULL;
+ mBootAction = nullptr;
}
};
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 4c05497..4b0c05f 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2166,7 +2166,7 @@
* reported back to the IT admin to be read.
*/
public void onInstallUpdateError(
- @InstallUpdateCallbackErrorConstants int errorCode, String errorMessage) {
+ @InstallUpdateCallbackErrorConstants int errorCode, @NonNull String errorMessage) {
}
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 032e5ac..d87171e 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3115,21 +3115,14 @@
* <p>
* The path to the file is contained in {@link Intent#getData()}.
*
- * @deprecated Starting in the {@link android.os.Build.VERSION_CODES#Q}
- * release, shared storage paths are sandboxed per application,
- * and this broadcast cannot correctly translate those sandboxed
- * paths. Callers will need to instead migrate to using
- * {@link MediaScannerConnection}.
+ * @deprecated Callers should migrate to inserting items directly into
+ * {@link MediaStore}, where they will be automatically scanned
+ * after each mutation.
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@Deprecated
public static final String ACTION_MEDIA_SCANNER_SCAN_FILE = "android.intent.action.MEDIA_SCANNER_SCAN_FILE";
- /** @hide */
- @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- @Deprecated
- public static final String ACTION_MEDIA_SCANNER_SCAN_VOLUME = "android.intent.action.MEDIA_SCANNER_SCAN_VOLUME";
-
/**
* Broadcast Action: The "Media Button" was pressed. Includes a single
* extra field, {@link #EXTRA_KEY_EVENT}, containing the key event that
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 3edd17a..1e6cea3 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -497,6 +497,36 @@
}
/**
+ * Returns an active staged session, or {@code null} if there is none.
+ *
+ * <p>Staged session is active iff:
+ * <ul>
+ * <li>It is committed.
+ * <li>It is not applied.
+ * <li>It is not failed.
+ * </ul>
+ *
+ * <p>In case of a multi-apk session, parent session will be returned.
+ */
+ public @Nullable SessionInfo getActiveStagedSession() {
+ final List<SessionInfo> stagedSessions = getStagedSessions();
+ for (SessionInfo s : stagedSessions) {
+ if (s.isStagedSessionApplied() || s.isStagedSessionFailed()) {
+ // Finalized session.
+ continue;
+ }
+ if (s.getParentSessionId() != SessionInfo.INVALID_ID) {
+ // Child session.
+ continue;
+ }
+ if (s.isCommitted()) {
+ return s;
+ }
+ }
+ return null;
+ }
+
+ /**
* Uninstall the given package, removing it completely from the device. This
* method is available to:
* <ul>
@@ -1770,6 +1800,9 @@
private String mStagedSessionErrorMessage;
/** {@hide} */
+ public boolean isCommitted;
+
+ /** {@hide} */
@UnsupportedAppUsage
public SessionInfo() {
}
@@ -1809,6 +1842,7 @@
isStagedSessionFailed = source.readBoolean();
mStagedSessionErrorCode = source.readInt();
mStagedSessionErrorMessage = source.readString();
+ isCommitted = source.readBoolean();
}
/**
@@ -2181,6 +2215,13 @@
mStagedSessionErrorMessage = errorMessage;
}
+ /**
+ * Whenever this session was committed.
+ */
+ public boolean isCommitted() {
+ return isCommitted;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -2218,6 +2259,7 @@
dest.writeBoolean(isStagedSessionFailed);
dest.writeInt(mStagedSessionErrorCode);
dest.writeString(mStagedSessionErrorMessage);
+ dest.writeBoolean(isCommitted);
}
public static final Parcelable.Creator<SessionInfo>
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index b480939..35d1eac 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -166,6 +166,9 @@
private static final float DEFAULT_PRE_Q_MIN_ASPECT_RATIO = 1.333f;
private static final float DEFAULT_PRE_Q_MIN_ASPECT_RATIO_WATCH = 1f;
+ private static final int DEFAULT_MIN_SDK_VERSION = 1;
+ private static final int DEFAULT_TARGET_SDK_VERSION = 0;
+
// TODO: switch outError users to PackageParserException
// TODO: refactor "codePath" to "apkPath"
@@ -466,6 +469,8 @@
public final int versionCodeMajor;
public final int revisionCode;
public final int installLocation;
+ public final int minSdkVersion;
+ public final int targetSdkVersion;
public final VerifierInfo[] verifiers;
public final SigningDetails signingDetails;
public final boolean coreApp;
@@ -484,7 +489,8 @@
int revisionCode, int installLocation, List<VerifierInfo> verifiers,
SigningDetails signingDetails, boolean coreApp,
boolean debuggable, boolean multiArch, boolean use32bitAbi,
- boolean useEmbeddedDex, boolean extractNativeLibs, boolean isolatedSplits) {
+ boolean useEmbeddedDex, boolean extractNativeLibs, boolean isolatedSplits,
+ int minSdkVersion, int targetSdkVersion) {
this.codePath = codePath;
this.packageName = packageName;
this.splitName = splitName;
@@ -505,6 +511,8 @@
this.extractNativeLibs = extractNativeLibs;
this.isolatedSplits = isolatedSplits;
this.isSplitRequired = isSplitRequired;
+ this.minSdkVersion = minSdkVersion;
+ this.targetSdkVersion = targetSdkVersion;
}
public long getLongVersionCode() {
@@ -1712,6 +1720,8 @@
int installLocation = PARSE_DEFAULT_INSTALL_LOCATION;
int versionCode = 0;
int versionCodeMajor = 0;
+ int targetSdkVersion = DEFAULT_TARGET_SDK_VERSION;
+ int minSdkVersion = DEFAULT_MIN_SDK_VERSION;
int revisionCode = 0;
boolean coreApp = false;
boolean debuggable = false;
@@ -1749,7 +1759,7 @@
}
}
- // Only search the tree when the tag is directly below <manifest>
+ // Only search the tree when the tag is the direct child of <manifest> tag
int type;
final int searchDepth = parser.getDepth() + 1;
@@ -1800,13 +1810,25 @@
PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
"<uses-split> tag requires 'android:name' attribute");
}
+ } else if (TAG_USES_SDK.equals(parser.getName())) {
+ for (int i = 0; i < attrs.getAttributeCount(); ++i) {
+ final String attr = attrs.getAttributeName(i);
+ if ("targetSdkVersion".equals(attr)) {
+ targetSdkVersion = attrs.getAttributeIntValue(i,
+ DEFAULT_TARGET_SDK_VERSION);
+ }
+ if ("minSdkVersion".equals(attr)) {
+ minSdkVersion = attrs.getAttributeIntValue(i, DEFAULT_MIN_SDK_VERSION);
+ }
+ }
}
}
return new ApkLite(codePath, packageSplit.first, packageSplit.second, isFeatureSplit,
configForSplit, usesSplitName, isSplitRequired, versionCode, versionCodeMajor,
revisionCode, installLocation, verifiers, signingDetails, coreApp, debuggable,
- multiArch, use32bitAbi, useEmbeddedDex, extractNativeLibs, isolatedSplits);
+ multiArch, use32bitAbi, useEmbeddedDex, extractNativeLibs, isolatedSplits,
+ minSdkVersion, targetSdkVersion);
}
/**
@@ -8350,6 +8372,7 @@
}
}
+ // TODO(b/129261524): Clean up API
public static PackageInfo generatePackageInfoFromApex(File apexFile, boolean collectCerts)
throws PackageParserException {
PackageInfo pi = new PackageInfo();
@@ -8369,25 +8392,27 @@
}
}
- // TODO(b/123086053) properly fill in the ApplicationInfo with data from AndroidManifest
+ PackageParser.ApkLite apk = PackageParser.parseApkLite(apexFile, parseFlags);
+
+ // Properly fill in the ApplicationInfo with data from AndroidManifest
// Add ApplicationInfo to the PackageInfo.
+ // TODO(b/129267599)
ApplicationInfo ai = new ApplicationInfo();
+ ai.packageName = apk.packageName;
ai.sourceDir = apexFile.getPath();
ai.flags = ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_INSTALLED;
ai.enabled = true;
- ai.targetSdkVersion = 28;
- ai.targetSandboxVersion = 0;
- pi.applicationInfo = ai;
-
-
- // TODO(b/123052859): We should avoid these repeated calls to parseApkLite each time
- // we want to generate information for APEX modules.
- PackageParser.ApkLite apk = PackageParser.parseApkLite(apexFile, parseFlags);
+ ai.minSdkVersion = apk.minSdkVersion;
+ ai.targetSdkVersion = apk.targetSdkVersion;
+ ai.targetSandboxVersion = PARSE_DEFAULT_TARGET_SANDBOX;
+ ai.setVersionCode(apk.getLongVersionCode());
pi.packageName = apk.packageName;
- ai.packageName = apk.packageName;
+ pi.splitNames = new String[]{apk.splitName};
pi.setLongVersionCode(apk.getLongVersionCode());
- ai.setVersionCode(apk.getLongVersionCode());
+ pi.applicationInfo = ai;
+ pi.coreApp = apk.coreApp;
+
if (collectCerts) {
if (apk.signingDetails.hasPastSigningCertificates()) {
diff --git a/core/java/android/content/pm/ShortcutManager.java b/core/java/android/content/pm/ShortcutManager.java
index df67117..f851799 100644
--- a/core/java/android/content/pm/ShortcutManager.java
+++ b/core/java/android/content/pm/ShortcutManager.java
@@ -15,8 +15,10 @@
*/
package android.content.pm;
+import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
@@ -565,6 +567,7 @@
*/
@NonNull
@SystemApi
+ @RequiresPermission(Manifest.permission.MANAGE_APP_PREDICTIONS)
public List<ShareShortcutInfo> getShareTargets(@NonNull IntentFilter filter) {
try {
return mService.getShareTargets(mContext.getPackageName(), filter,
diff --git a/core/java/android/database/sqlite/SQLiteQueryBuilder.java b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
index a73a719..bad80b8 100644
--- a/core/java/android/database/sqlite/SQLiteQueryBuilder.java
+++ b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
@@ -80,7 +80,7 @@
* Get if the query is marked as {@code DISTINCT}, as last configured by
* {@link #setDistinct(boolean)}.
*/
- public boolean getDistinct() {
+ public boolean isDistinct() {
return mDistinct;
}
@@ -215,7 +215,7 @@
}
/**
- * Sets the cursor factory to be used for the query, as last configured by
+ * Gets the cursor factory to be used for the query, as last configured by
* {@link #setCursorFactory(android.database.sqlite.SQLiteDatabase.CursorFactory)}.
*/
public @Nullable SQLiteDatabase.CursorFactory getCursorFactory() {
@@ -251,7 +251,7 @@
* Get if the query is marked as strict, as last configured by
* {@link #setStrict(boolean)}.
*/
- public boolean getStrict() {
+ public boolean isStrict() {
return mStrict;
}
diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java
index 9e0d95b..c3166e9 100644
--- a/core/java/android/net/Uri.java
+++ b/core/java/android/net/Uri.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.annotation.UnsupportedAppUsage;
import android.content.Intent;
import android.os.Environment;
@@ -380,9 +381,10 @@
* returned as {@code tel:xxx-xxx-xxxx} and {@code http://example.com/path/to/item/} is
* returned as {@code http://example.com/...}.
* @return the common forms PII redacted string of this URI
+ * @hide
*/
- @NonNull
- public String toSafeString() {
+ @SystemApi
+ public @NonNull String toSafeString() {
String scheme = getScheme();
String ssp = getSchemeSpecificPart();
if (scheme != null) {
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 6a01e56..7cc7ccd 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -30,6 +30,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
+import android.os.storage.IStorageManager;
import android.provider.Settings;
import android.telephony.euicc.EuiccManager;
import android.text.TextUtils;
@@ -38,6 +39,8 @@
import android.view.Display;
import android.view.WindowManager;
+import com.android.internal.content.PackageHelper;
+
import libcore.io.Streams;
import java.io.ByteArrayInputStream;
@@ -854,6 +857,21 @@
/** {@hide} */
public static void rebootPromptAndWipeUserData(Context context, String reason)
throws IOException {
+ boolean checkpointing = false;
+
+ // If we are running in checkpointing mode, we should not prompt a wipe.
+ // Checkpointing may save us. If it doesn't, we will wind up here again.
+ try {
+ IStorageManager storageManager = PackageHelper.getStorageManager();
+ if (storageManager.needsCheckpoint()) {
+ Log.i(TAG, "Rescue Party requested wipe. Aborting update instead.");
+ storageManager.abortChanges("rescueparty", false);
+ }
+ return;
+ } catch (RemoteException e) {
+ Log.i(TAG, "Failed to handle with checkpointing. Continuing with wipe.");
+ }
+
String reasonArg = null;
if (!TextUtils.isEmpty(reason)) {
reasonArg = "--reason=" + sanitizeArg(reason);
diff --git a/core/java/android/os/image/DynamicSystemClient.java b/core/java/android/os/image/DynamicSystemClient.java
index 33a6ee8..87367ac 100644
--- a/core/java/android/os/image/DynamicSystemClient.java
+++ b/core/java/android/os/image/DynamicSystemClient.java
@@ -19,6 +19,7 @@
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.content.ComponentName;
@@ -31,6 +32,7 @@
import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
+import android.os.ParcelableException;
import android.os.RemoteException;
import android.util.Slog;
@@ -100,9 +102,10 @@
* @param status status code, also defined in {@code DynamicSystemClient}.
* @param cause cause code, also defined in {@code DynamicSystemClient}.
* @param progress number of bytes installed.
+ * @param detail additional detail about the error if available, otherwise null.
*/
void onStatusChanged(@InstallationStatus int status, @StatusChangedCause int cause,
- @BytesLong long progress);
+ @BytesLong long progress, @Nullable Throwable detail);
}
/*
@@ -177,6 +180,12 @@
*/
public static final String KEY_INSTALLED_SIZE = "KEY_INSTALLED_SIZE";
+ /**
+ * Message key, used when the service is sending exception detail to the client.
+ * @hide
+ */
+ public static final String KEY_EXCEPTION_DETAIL = "KEY_EXCEPTION_DETAIL";
+
/*
* Intent Actions
*/
@@ -248,7 +257,7 @@
} catch (RemoteException e) {
Slog.e(TAG, "Unable to get status from installation service");
mExecutor.execute(() -> {
- mListener.onStatusChanged(STATUS_UNKNOWN, CAUSE_ERROR_IPC, 0);
+ mListener.onStatusChanged(STATUS_UNKNOWN, CAUSE_ERROR_IPC, 0, e);
});
}
}
@@ -396,14 +405,19 @@
int status = msg.arg1;
int cause = msg.arg2;
// obj is non-null
- long progress = ((Bundle) msg.obj).getLong(KEY_INSTALLED_SIZE);
+ Bundle bundle = (Bundle) msg.obj;
+ long progress = bundle.getLong(KEY_INSTALLED_SIZE);
+ ParcelableException t = (ParcelableException) bundle.getSerializable(
+ KEY_EXCEPTION_DETAIL);
+
+ Throwable detail = t == null ? null : t.getCause();
if (mExecutor != null) {
mExecutor.execute(() -> {
- mListener.onStatusChanged(status, cause, progress);
+ mListener.onStatusChanged(status, cause, progress, detail);
});
} else {
- mListener.onStatusChanged(status, cause, progress);
+ mListener.onStatusChanged(status, cause, progress, detail);
}
break;
default:
diff --git a/core/java/android/os/storage/IStorageManager.aidl b/core/java/android/os/storage/IStorageManager.aidl
index 25f67f8..9db4111 100644
--- a/core/java/android/os/storage/IStorageManager.aidl
+++ b/core/java/android/os/storage/IStorageManager.aidl
@@ -193,4 +193,6 @@
void commitChanges() = 83;
boolean supportsCheckpoint() = 84;
void startCheckpoint(int numTries) = 85;
+ boolean needsCheckpoint() = 86;
+ void abortChanges(in String message, boolean retry) = 87;
}
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index dc5fdc0..7feeeee 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -115,9 +115,9 @@
*/
public static final String VOLUME_EXTERNAL = "external";
- /** {@hide} */ @TestApi
+ /** {@hide} */
public static final String SCAN_FILE_CALL = "scan_file";
- /** {@hide} */ @TestApi
+ /** {@hide} */
public static final String SCAN_VOLUME_CALL = "scan_volume";
/**
@@ -126,7 +126,6 @@
*
* {@hide}
*/
- @TestApi
public static final String EXTRA_ORIGINATED_FROM_SHELL =
"android.intent.extra.originated_from_shell";
@@ -3539,12 +3538,32 @@
}
/** @hide */
+ @TestApi
public static Uri scanFile(Context context, File file) {
+ return scan(context, SCAN_FILE_CALL, file, false);
+ }
+
+ /** @hide */
+ @TestApi
+ public static Uri scanFileFromShell(Context context, File file) {
+ return scan(context, SCAN_FILE_CALL, file, true);
+ }
+
+ /** @hide */
+ @TestApi
+ public static void scanVolume(Context context, File file) {
+ scan(context, SCAN_VOLUME_CALL, file, false);
+ }
+
+ /** @hide */
+ private static Uri scan(Context context, String method, File file,
+ boolean originatedFromShell) {
final ContentResolver resolver = context.getContentResolver();
try (ContentProviderClient client = resolver.acquireContentProviderClient(AUTHORITY)) {
final Bundle in = new Bundle();
in.putParcelable(Intent.EXTRA_STREAM, Uri.fromFile(file));
- final Bundle out = client.call(SCAN_FILE_CALL, null, in);
+ in.putBoolean(EXTRA_ORIGINATED_FROM_SHELL, originatedFromShell);
+ final Bundle out = client.call(method, null, in);
return out.getParcelable(Intent.EXTRA_STREAM);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
diff --git a/core/java/android/service/carrier/CarrierService.java b/core/java/android/service/carrier/CarrierService.java
index c351d89..aeb186b 100644
--- a/core/java/android/service/carrier/CarrierService.java
+++ b/core/java/android/service/carrier/CarrierService.java
@@ -114,9 +114,7 @@
* this UX, so a carrier app must be sure to call with active set to false
* sometime after calling with it set to true.
* <p>
- * Requires Permission:
- * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
- * or the calling app has carrier privileges.
+ * Requires Permission: calling app has carrier privileges.
*
* @param active Whether the carrier network change is or shortly will be
* active. Set this value to true to begin showing
diff --git a/core/java/android/service/watchdog/ExplicitHealthCheckService.java b/core/java/android/service/watchdog/ExplicitHealthCheckService.java
new file mode 100644
index 0000000..015fba1
--- /dev/null
+++ b/core/java/android/service/watchdog/ExplicitHealthCheckService.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.watchdog;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A service to provide packages supporting explicit health checks and route checks to these
+ * packages on behalf of the package watchdog.
+ *
+ * <p>To extend this class, you must declare the service in your manifest file with the
+ * {@link android.Manifest.permission#BIND_EXPLICIT_HEALTH_CHECK_SERVICE} permission,
+ * and include an intent filter with the {@link #SERVICE_INTERFACE} action. In adddition,
+ * your implementation must live in {@link PackageManger#SYSTEM_SHARED_LIBRARY_SERVICES}.
+ * For example:</p>
+ * <pre>
+ * <service android:name=".FooExplicitHealthCheckService"
+ * android:exported="true"
+ * android:priority="100"
+ * android:permission="android.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE">
+ * <intent-filter>
+ * <action android:name="android.service.watchdog.ExplicitHealthCheckService" />
+ * </intent-filter>
+ * </service>
+ * </pre>
+ * @hide
+ */
+@SystemApi
+public abstract class ExplicitHealthCheckService extends Service {
+
+ private static final String TAG = "ExplicitHealthCheckService";
+
+ /**
+ * {@link Bundle} key for a {@link List} of {@link String} value.
+ *
+ * {@hide}
+ */
+ public static final String EXTRA_SUPPORTED_PACKAGES =
+ "android.service.watchdog.extra.supported_packages";
+
+ /**
+ * {@link Bundle} key for a {@link List} of {@link String} value.
+ *
+ * {@hide}
+ */
+ public static final String EXTRA_REQUESTED_PACKAGES =
+ "android.service.watchdog.extra.requested_packages";
+
+ /**
+ * {@link Bundle} key for a {@link String} value.
+ *
+ * {@hide}
+ */
+ public static final String EXTRA_HEALTH_CHECK_PASSED_PACKAGE =
+ "android.service.watchdog.extra.health_check_passed_package";
+
+ /**
+ * The Intent action that a service must respond to. Add it to the intent filter of the service
+ * in its manifest.
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+ public static final String SERVICE_INTERFACE =
+ "android.service.watchdog.ExplicitHealthCheckService";
+
+ /**
+ * The permission that a service must require to ensure that only Android system can bind to it.
+ * If this permission is not enforced in the AndroidManifest of the service, the system will
+ * skip that service.
+ */
+ public static final String BIND_PERMISSION =
+ "android.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE";
+
+ private final ExplicitHealthCheckServiceWrapper mWrapper =
+ new ExplicitHealthCheckServiceWrapper();
+
+ /**
+ * Called when the system requests an explicit health check for {@code packageName}.
+ *
+ * <p> When {@code packageName} passes the check, implementors should call
+ * {@link #notifyHealthCheckPassed} to inform the system.
+ *
+ * <p> It could take many hours before a {@code packageName} passes a check and implementors
+ * should never drop requests unless {@link onCancel} is called or the service dies.
+ *
+ * <p> Requests should not be queued and additional calls while expecting a result for
+ * {@code packageName} should have no effect.
+ */
+ public abstract void onRequestHealthCheck(@NonNull String packageName);
+
+ /**
+ * Called when the system cancels the explicit health check request for {@code packageName}.
+ * Should do nothing if there are is no active request for {@code packageName}.
+ */
+ public abstract void onCancelHealthCheck(@NonNull String packageName);
+
+ /**
+ * Called when the system requests for all the packages supporting explicit health checks. The
+ * system may request an explicit health check for any of these packages with
+ * {@link #onRequestHealthCheck}.
+ *
+ * @return all packages supporting explicit health checks
+ */
+ @NonNull public abstract List<String> onGetSupportedPackages();
+
+ /**
+ * Called when the system requests for all the packages that it has currently requested
+ * an explicit health check for.
+ *
+ * @return all packages expecting an explicit health check result
+ */
+ @NonNull public abstract List<String> onGetRequestedPackages();
+
+ private final Handler mHandler = new Handler(Looper.getMainLooper(), null, true);
+ @Nullable private RemoteCallback mCallback;
+
+ @Override
+ @NonNull
+ public final IBinder onBind(@NonNull Intent intent) {
+ return mWrapper;
+ }
+
+ /**
+ * Implementors should call this to notify the system when explicit health check passes
+ * for {@code packageName};
+ */
+ public final void notifyHealthCheckPassed(@NonNull String packageName) {
+ mHandler.post(() -> {
+ if (mCallback != null) {
+ Objects.requireNonNull(packageName,
+ "Package passing explicit health check must be non-null");
+ Bundle bundle = new Bundle();
+ bundle.putString(EXTRA_HEALTH_CHECK_PASSED_PACKAGE, packageName);
+ mCallback.sendResult(bundle);
+ } else {
+ Log.wtf(TAG, "System missed explicit health check result for " + packageName);
+ }
+ });
+ }
+
+ private class ExplicitHealthCheckServiceWrapper extends IExplicitHealthCheckService.Stub {
+ @Override
+ public void setCallback(RemoteCallback callback) throws RemoteException {
+ mHandler.post(() -> {
+ mCallback = callback;
+ });
+ }
+
+ @Override
+ public void request(String packageName) throws RemoteException {
+ mHandler.post(() -> ExplicitHealthCheckService.this.onRequestHealthCheck(packageName));
+ }
+
+ @Override
+ public void cancel(String packageName) throws RemoteException {
+ mHandler.post(() -> ExplicitHealthCheckService.this.onCancelHealthCheck(packageName));
+ }
+
+ @Override
+ public void getSupportedPackages(RemoteCallback callback) throws RemoteException {
+ mHandler.post(() -> sendPackages(callback, EXTRA_SUPPORTED_PACKAGES,
+ ExplicitHealthCheckService.this.onGetSupportedPackages()));
+ }
+
+ @Override
+ public void getRequestedPackages(RemoteCallback callback) throws RemoteException {
+ mHandler.post(() -> sendPackages(callback, EXTRA_REQUESTED_PACKAGES,
+ ExplicitHealthCheckService.this.onGetRequestedPackages()));
+ }
+
+ private void sendPackages(RemoteCallback callback, String key, List<String> packages) {
+ Objects.requireNonNull(packages,
+ "Supported and requested package list must be non-null");
+ Bundle bundle = new Bundle();
+ bundle.putStringArrayList(key, new ArrayList<>(packages));
+ callback.sendResult(bundle);
+ }
+ }
+}
diff --git a/core/java/android/service/watchdog/IExplicitHealthCheckService.aidl b/core/java/android/service/watchdog/IExplicitHealthCheckService.aidl
new file mode 100644
index 0000000..78c0328d
--- /dev/null
+++ b/core/java/android/service/watchdog/IExplicitHealthCheckService.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.watchdog;
+
+import android.os.RemoteCallback;
+
+/**
+ * @hide
+ */
+oneway interface IExplicitHealthCheckService
+{
+ void setCallback(in @nullable RemoteCallback callback);
+ void request(String packageName);
+ void cancel(String packageName);
+ void getSupportedPackages(in RemoteCallback callback);
+ void getRequestedPackages(in RemoteCallback callback);
+}
diff --git a/core/java/android/view/accessibility/AccessibilityCache.java b/core/java/android/view/accessibility/AccessibilityCache.java
index df8690d..f9e601f 100644
--- a/core/java/android/view/accessibility/AccessibilityCache.java
+++ b/core/java/android/view/accessibility/AccessibilityCache.java
@@ -105,11 +105,7 @@
Log.i(LOG_TAG, "Caching window: " + window.getId());
}
final int windowId = window.getId();
- AccessibilityWindowInfo oldWindow = mWindowCache.get(windowId);
- if (oldWindow != null) {
- oldWindow.recycle();
- }
- mWindowCache.put(windowId, AccessibilityWindowInfo.obtain(window));
+ mWindowCache.put(windowId, new AccessibilityWindowInfo(window));
}
}
@@ -225,7 +221,7 @@
if (info != null) {
// Return a copy since the client calls to AccessibilityNodeInfo#recycle()
// will wipe the data of the cached info.
- info = AccessibilityNodeInfo.obtain(info);
+ info = new AccessibilityNodeInfo(info);
}
if (DEBUG) {
Log.i(LOG_TAG, "get(" + accessibilityNodeId + ") = " + info);
@@ -257,7 +253,7 @@
List<AccessibilityWindowInfo> windows = new ArrayList<>(sortedWindowCount);
for (int i = sortedWindowCount - 1; i >= 0; i--) {
AccessibilityWindowInfo window = sortedWindows.valueAt(i);
- windows.add(AccessibilityWindowInfo.obtain(window));
+ windows.add(new AccessibilityWindowInfo(window));
sortedWindows.removeAt(i);
}
@@ -271,7 +267,7 @@
synchronized (mLock) {
AccessibilityWindowInfo window = mWindowCache.get(windowId);
if (window != null) {
- return AccessibilityWindowInfo.obtain(window);
+ return new AccessibilityWindowInfo(window);
}
return null;
}
@@ -326,14 +322,12 @@
final long oldParentId = oldInfo.getParentNodeId();
if (info.getParentNodeId() != oldParentId) {
clearSubTreeLocked(windowId, oldParentId);
- } else {
- oldInfo.recycle();
}
}
// Cache a copy since the client calls to AccessibilityNodeInfo#recycle()
// will wipe the data of the cached info.
- AccessibilityNodeInfo clone = AccessibilityNodeInfo.obtain(info);
+ AccessibilityNodeInfo clone = new AccessibilityNodeInfo(info);
nodes.put(sourceId, clone);
if (clone.isAccessibilityFocused()) {
if (mAccessibilityFocus != AccessibilityNodeInfo.UNDEFINED_ITEM_ID
@@ -371,12 +365,7 @@
}
private void clearWindowCache() {
- final int windowCount = mWindowCache.size();
- for (int i = windowCount - 1; i >= 0; i--) {
- AccessibilityWindowInfo window = mWindowCache.valueAt(i);
- window.recycle();
- mWindowCache.removeAt(i);
- }
+ mWindowCache.clear();
mIsAllWindowsCached = false;
}
@@ -391,13 +380,6 @@
if (nodes == null) {
return;
}
- // Recycle the nodes before clearing the cache.
- final int nodeCount = nodes.size();
- for (int i = nodeCount - 1; i >= 0; i--) {
- AccessibilityNodeInfo info = nodes.valueAt(i);
- nodes.removeAt(i);
- info.recycle();
- }
mNodeCache.remove(windowId);
}
@@ -440,11 +422,9 @@
for (int i = 0; i < childCount; i++) {
final long childNodeId = current.getChildId(i);
if (clearSubTreeRecursiveLocked(nodes, childNodeId)) {
- current.recycle();
return true;
}
}
- current.recycle();
return false;
}
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index a8a787e..385d491 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -654,6 +654,7 @@
*/
private static final int VIRTUAL_DESCENDANT_ID_SHIFT = 32;
+ // TODO(b/129300068): Remove sNumInstancesInUse.
private static AtomicInteger sNumInstancesInUse;
/**
@@ -766,6 +767,11 @@
/* do nothing */
}
+ /** @hide */
+ AccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ init(info);
+ }
+
/**
* Sets the source.
* <p>
diff --git a/core/java/android/view/accessibility/AccessibilityWindowInfo.java b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
index 4383f8a..1e2321c 100644
--- a/core/java/android/view/accessibility/AccessibilityWindowInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
@@ -96,6 +96,7 @@
private static final int MAX_POOL_SIZE = 10;
private static final SynchronizedPool<AccessibilityWindowInfo> sPool =
new SynchronizedPool<AccessibilityWindowInfo>(MAX_POOL_SIZE);
+ // TODO(b/129300068): Remove sNumInstancesInUse.
private static AtomicInteger sNumInstancesInUse;
// Data.
@@ -115,6 +116,11 @@
/* do nothing - hide constructor */
}
+ /** @hide */
+ AccessibilityWindowInfo(AccessibilityWindowInfo info) {
+ init(info);
+ }
+
/**
* Gets the title of the window.
*
@@ -448,26 +454,7 @@
*/
public static AccessibilityWindowInfo obtain(AccessibilityWindowInfo info) {
AccessibilityWindowInfo infoClone = obtain();
-
- infoClone.mType = info.mType;
- infoClone.mLayer = info.mLayer;
- infoClone.mBooleanProperties = info.mBooleanProperties;
- infoClone.mId = info.mId;
- infoClone.mParentId = info.mParentId;
- infoClone.mBoundsInScreen.set(info.mBoundsInScreen);
- infoClone.mTitle = info.mTitle;
- infoClone.mAnchorId = info.mAnchorId;
-
- if (info.mChildIds != null && info.mChildIds.size() > 0) {
- if (infoClone.mChildIds == null) {
- infoClone.mChildIds = info.mChildIds.clone();
- } else {
- infoClone.mChildIds.addAll(info.mChildIds);
- }
- }
-
- infoClone.mConnectionId = info.mConnectionId;
-
+ infoClone.init(info);
return infoClone;
}
@@ -529,6 +516,32 @@
parcel.writeInt(mConnectionId);
}
+ /**
+ * Initializes this instance from another one.
+ *
+ * @param other The other instance.
+ */
+ private void init(AccessibilityWindowInfo other) {
+ mType = other.mType;
+ mLayer = other.mLayer;
+ mBooleanProperties = other.mBooleanProperties;
+ mId = other.mId;
+ mParentId = other.mParentId;
+ mBoundsInScreen.set(other.mBoundsInScreen);
+ mTitle = other.mTitle;
+ mAnchorId = other.mAnchorId;
+
+ if (other.mChildIds != null && other.mChildIds.size() > 0) {
+ if (mChildIds == null) {
+ mChildIds = other.mChildIds.clone();
+ } else {
+ mChildIds.addAll(other.mChildIds);
+ }
+ }
+
+ mConnectionId = other.mConnectionId;
+ }
+
private void initFromParcel(Parcel parcel) {
mType = parcel.readInt();
mLayer = parcel.readInt();
diff --git a/core/java/android/view/inspector/PropertyReader.java b/core/java/android/view/inspector/PropertyReader.java
index b5020ce..5be0e3f 100644
--- a/core/java/android/view/inspector/PropertyReader.java
+++ b/core/java/android/view/inspector/PropertyReader.java
@@ -133,10 +133,11 @@
void readColor(int id, @ColorInt int value);
/**
- * Read a color packed into a {@link ColorLong} as a property.
+ * Read a color packed into a {@code ColorLong} as a property.
*
* @param id Identifier of the property from a {@link PropertyMapper}
- * @param value Value of the property
+ * @param value Value of the property packed as a {@code ColorLong}. See the
+ * {@link Color} class for details of the packing.
* @throws PropertyTypeMismatchException If the property ID is not mapped as a color
*/
void readColor(int id, @ColorLong long value);
diff --git a/core/java/com/android/internal/os/KernelCpuThreadReader.java b/core/java/com/android/internal/os/KernelCpuThreadReader.java
index 248e026..e4de158 100644
--- a/core/java/com/android/internal/os/KernelCpuThreadReader.java
+++ b/core/java/com/android/internal/os/KernelCpuThreadReader.java
@@ -32,15 +32,18 @@
import java.util.function.Predicate;
/**
- * Given a process, will iterate over the child threads of the process, and return the CPU usage
- * statistics for each child thread. The CPU usage statistics contain the amount of time spent in a
- * frequency band.
+ * Iterates over processes, and all threads owned by those processes, and return the CPU usage for
+ * each thread. The CPU usage statistics contain the amount of time spent in a frequency band. CPU
+ * usage is collected using {@link ProcTimeInStateReader}.
+ *
+ * <p>We only collect CPU data for processes and threads that are owned by certain UIDs. These UIDs
+ * are configured via {@link #setUidPredicate}.
*
* <p>Frequencies are bucketed together to reduce the amount of data created. This means that we
- * return less frequencies than provided by {@link ProcTimeInStateReader}. The number of
- * frequencies is configurable by {@link #setNumBuckets}. Frequencies are reported as the lowest
- * frequency in that range. Frequencies are spread as evenly as possible across the buckets. The
- * buckets do not cross over the little/big frequencies reported.
+ * return less frequencies than provided by {@link ProcTimeInStateReader}. The number of frequencies
+ * is configurable by {@link #setNumBuckets}. Frequencies are reported as the lowest frequency in
+ * that range. Frequencies are spread as evenly as possible across the buckets. The buckets do not
+ * cross over the little/big frequencies reported.
*
* <p>N.B.: In order to bucket across little/big frequencies correctly, we assume that the {@code
* time_in_state} file contains every little core frequency in ascending order, followed by every
@@ -60,56 +63,39 @@
private static final String CPU_STATISTICS_FILENAME = "time_in_state";
/**
- * The name of the file to read process command line invocation from, must be found in
- * {@code /proc/$PID/}
+ * The name of the file to read process command line invocation from, must be found in {@code
+ * /proc/$PID/}
*/
private static final String PROCESS_NAME_FILENAME = "cmdline";
/**
- * The name of the file to read thread name from, must be found in
- * {@code /proc/$PID/task/$TID}
+ * The name of the file to read thread name from, must be found in {@code /proc/$PID/task/$TID}
*/
private static final String THREAD_NAME_FILENAME = "comm";
- /**
- * Glob pattern for the process directory names under {@code proc}
- */
+ /** Glob pattern for the process directory names under {@code proc} */
private static final String PROCESS_DIRECTORY_FILTER = "[0-9]*";
- /**
- * Default process name when the name can't be read
- */
+ /** Default process name when the name can't be read */
private static final String DEFAULT_PROCESS_NAME = "unknown_process";
- /**
- * Default thread name when the name can't be read
- */
+ /** Default thread name when the name can't be read */
private static final String DEFAULT_THREAD_NAME = "unknown_thread";
- /**
- * Default mount location of the {@code proc} filesystem
- */
+ /** Default mount location of the {@code proc} filesystem */
private static final Path DEFAULT_PROC_PATH = Paths.get("/proc");
- /**
- * The initial {@code time_in_state} file for {@link ProcTimeInStateReader}
- */
+ /** The initial {@code time_in_state} file for {@link ProcTimeInStateReader} */
private static final Path DEFAULT_INITIAL_TIME_IN_STATE_PATH =
DEFAULT_PROC_PATH.resolve("self/time_in_state");
- /**
- * Value returned when there was an error getting an integer ID value (e.g. PID, UID)
- */
+ /** Value returned when there was an error getting an integer ID value (e.g. PID, UID) */
private static final int ID_ERROR = -1;
- /**
- * Thread ID used when reporting CPU used by other threads
- */
+ /** Thread ID used when reporting CPU used by other threads */
private static final int OTHER_THREADS_ID = -1;
- /**
- * Thread name used when reporting CPU used by other threads
- */
+ /** Thread name used when reporting CPU used by other threads */
private static final String OTHER_THREADS_NAME = "__OTHER_THREADS";
/**
@@ -124,9 +110,7 @@
*/
private int mMinimumTotalCpuUsageMillis;
- /**
- * Where the proc filesystem is mounted
- */
+ /** Where the proc filesystem is mounted */
private final Path mProcPath;
/**
@@ -135,14 +119,10 @@
*/
private int[] mFrequenciesKhz;
- /**
- * Used to read and parse {@code time_in_state} files
- */
+ /** Used to read and parse {@code time_in_state} files */
private final ProcTimeInStateReader mProcTimeInStateReader;
- /**
- * Used to sort frequencies and usage times into buckets
- */
+ /** Used to sort frequencies and usage times into buckets */
private FrequencyBucketCreator mFrequencyBucketCreator;
private final Injector mInjector;
@@ -150,10 +130,9 @@
/**
* Create with a path where `proc` is mounted. Used primarily for testing
*
- * @param procPath where `proc` is mounted (to find, see {@code mount | grep
- * ^proc})
+ * @param procPath where `proc` is mounted (to find, see {@code mount | grep ^proc})
* @param initialTimeInStatePath where the initial {@code time_in_state} file exists to define
- * format
+ * format
*/
@VisibleForTesting
public KernelCpuThreadReader(
@@ -162,7 +141,8 @@
int minimumTotalCpuUsageMillis,
Path procPath,
Path initialTimeInStatePath,
- Injector injector) throws IOException {
+ Injector injector)
+ throws IOException {
mUidPredicate = uidPredicate;
mMinimumTotalCpuUsageMillis = minimumTotalCpuUsageMillis;
mProcPath = procPath;
@@ -205,7 +185,7 @@
* #setUidPredicate}.
*/
@Nullable
- public ArrayList<ProcessCpuUsage> getProcessCpuUsageByUids() {
+ public ArrayList<ProcessCpuUsage> getProcessCpuUsage() {
if (DEBUG) {
Slog.d(TAG, "Reading CPU thread usages for processes owned by UIDs");
}
@@ -213,7 +193,7 @@
final ArrayList<ProcessCpuUsage> processCpuUsages = new ArrayList<>();
try (DirectoryStream<Path> processPaths =
- Files.newDirectoryStream(mProcPath, PROCESS_DIRECTORY_FILTER)) {
+ Files.newDirectoryStream(mProcPath, PROCESS_DIRECTORY_FILTER)) {
for (Path processPath : processPaths) {
final int processId = getProcessId(processPath);
final int uid = mInjector.getUidForPid(processId);
@@ -231,7 +211,7 @@
}
}
} catch (IOException e) {
- Slog.w("Failed to iterate over process paths", e);
+ Slog.w(TAG, "Failed to iterate over process paths", e);
return null;
}
@@ -248,30 +228,68 @@
}
/**
- * Read all of the CPU usage statistics for each child thread of the current process
- *
- * @return process CPU usage containing usage of all child threads
+ * Get the CPU frequencies that correspond to the times reported in {@link
+ * ThreadCpuUsage#usageTimesMillis}
*/
@Nullable
- public ProcessCpuUsage getCurrentProcessCpuUsage() {
- return getProcessCpuUsage(mProcPath.resolve("self"), mInjector.myPid(), mInjector.myUid());
+ public int[] getCpuFrequenciesKhz() {
+ return mFrequenciesKhz;
+ }
+
+ /** Set the number of frequency buckets to use */
+ void setNumBuckets(int numBuckets) {
+ if (numBuckets < 1) {
+ Slog.w(TAG, "Number of buckets must be at least 1, but was " + numBuckets);
+ return;
+ }
+ // If `numBuckets` hasn't changed since the last set, do nothing
+ if (mFrequenciesKhz != null && mFrequenciesKhz.length == numBuckets) {
+ return;
+ }
+ mFrequencyBucketCreator =
+ new FrequencyBucketCreator(mProcTimeInStateReader.getFrequenciesKhz(), numBuckets);
+ mFrequenciesKhz =
+ mFrequencyBucketCreator.getBucketMinFrequencies(
+ mProcTimeInStateReader.getFrequenciesKhz());
+ }
+
+ /** Set the UID predicate for {@link #getProcessCpuUsage} */
+ void setUidPredicate(Predicate<Integer> uidPredicate) {
+ mUidPredicate = uidPredicate;
+ }
+
+ /**
+ * If a thread has strictly less than {@code minimumTotalCpuUsageMillis} total CPU usage, it
+ * will not be reported
+ */
+ void setMinimumTotalCpuUsageMillis(int minimumTotalCpuUsageMillis) {
+ if (minimumTotalCpuUsageMillis < 0) {
+ Slog.w(TAG, "Negative minimumTotalCpuUsageMillis: " + minimumTotalCpuUsageMillis);
+ return;
+ }
+ mMinimumTotalCpuUsageMillis = minimumTotalCpuUsageMillis;
}
/**
* Read all of the CPU usage statistics for each child thread of a process
*
* @param processPath the {@code /proc} path of the thread
- * @param processId the ID of the process
- * @param uid the ID of the user who owns the process
+ * @param processId the ID of the process
+ * @param uid the ID of the user who owns the process
* @return process CPU usage containing usage of all child threads. Null if the process exited
- * and its {@code proc} directory was removed while collecting information
+ * and its {@code proc} directory was removed while collecting information
*/
@Nullable
private ProcessCpuUsage getProcessCpuUsage(Path processPath, int processId, int uid) {
if (DEBUG) {
- Slog.d(TAG, "Reading CPU thread usages with directory " + processPath
- + " process ID " + processId
- + " and user ID " + uid);
+ Slog.d(
+ TAG,
+ "Reading CPU thread usages with directory "
+ + processPath
+ + " process ID "
+ + processId
+ + " and user ID "
+ + uid);
}
int[] filteredThreadsCpuUsage = null;
@@ -305,64 +323,15 @@
// Add the filtered out thread CPU usage under an "other threads" ThreadCpuUsage
if (filteredThreadsCpuUsage != null) {
- threadCpuUsages.add(new ThreadCpuUsage(
- OTHER_THREADS_ID, OTHER_THREADS_NAME, filteredThreadsCpuUsage));
+ threadCpuUsages.add(
+ new ThreadCpuUsage(
+ OTHER_THREADS_ID, OTHER_THREADS_NAME, filteredThreadsCpuUsage));
}
if (DEBUG) {
Slog.d(TAG, "Read CPU usage of " + threadCpuUsages.size() + " threads");
}
- return new ProcessCpuUsage(
- processId,
- getProcessName(processPath),
- uid,
- threadCpuUsages);
- }
-
- /**
- * Set the number of frequency buckets to use
- */
- void setNumBuckets(int numBuckets) {
- if (numBuckets < 1) {
- Slog.w(TAG, "Number of buckets must be at least 1, but was " + numBuckets);
- return;
- }
- // If `numBuckets` hasn't changed since the last set, do nothing
- if (mFrequenciesKhz != null && mFrequenciesKhz.length == numBuckets) {
- return;
- }
- mFrequencyBucketCreator = new FrequencyBucketCreator(
- mProcTimeInStateReader.getFrequenciesKhz(), numBuckets);
- mFrequenciesKhz = mFrequencyBucketCreator.getBucketMinFrequencies(
- mProcTimeInStateReader.getFrequenciesKhz());
- }
-
- /**
- * Set the UID predicate for {@link #getProcessCpuUsageByUids}
- */
- void setUidPredicate(Predicate<Integer> uidPredicate) {
- mUidPredicate = uidPredicate;
- }
-
- /**
- * If a thread has strictly less than {@code minimumTotalCpuUsageMillis} total CPU usage, it
- * will not be reported
- */
- void setMinimumTotalCpuUsageMillis(int minimumTotalCpuUsageMillis) {
- if (minimumTotalCpuUsageMillis < 0) {
- Slog.w(TAG, "Negative minimumTotalCpuUsageMillis: " + minimumTotalCpuUsageMillis);
- return;
- }
- mMinimumTotalCpuUsageMillis = minimumTotalCpuUsageMillis;
- }
-
- /**
- * Get the CPU frequencies that correspond to the times reported in
- * {@link ThreadCpuUsage#usageTimesMillis}
- */
- @Nullable
- public int[] getCpuFrequenciesKhz() {
- return mFrequenciesKhz;
+ return new ProcessCpuUsage(processId, getProcessName(processPath), uid, threadCpuUsages);
}
/**
@@ -370,7 +339,7 @@
*
* @param threadDirectory the {@code /proc} directory of the thread
* @return thread CPU usage. Null if the thread exited and its {@code proc} directory was
- * removed while collecting information
+ * removed while collecting information
*/
@Nullable
private ThreadCpuUsage getThreadCpuUsage(Path threadDirectory) {
@@ -398,27 +367,21 @@
return new ThreadCpuUsage(threadId, threadName, cpuUsages);
}
- /**
- * Get the command used to start a process
- */
+ /** Get the command used to start a process */
private String getProcessName(Path processPath) {
final Path processNamePath = processPath.resolve(PROCESS_NAME_FILENAME);
- final String processName =
- ProcStatsUtil.readSingleLineProcFile(processNamePath.toString());
+ final String processName = ProcStatsUtil.readSingleLineProcFile(processNamePath.toString());
if (processName != null) {
return processName;
}
return DEFAULT_PROCESS_NAME;
}
- /**
- * Get the name of a thread, given the {@code /proc} path of the thread
- */
+ /** Get the name of a thread, given the {@code /proc} path of the thread */
private String getThreadName(Path threadPath) {
final Path threadNamePath = threadPath.resolve(THREAD_NAME_FILENAME);
- final String threadName =
- ProcStatsUtil.readNullSeparatedFile(threadNamePath.toString());
+ final String threadName = ProcStatsUtil.readNullSeparatedFile(threadNamePath.toString());
if (threadName == null) {
return DEFAULT_THREAD_NAME;
}
@@ -441,9 +404,8 @@
}
}
- /**
- * Get the sum of all CPU usage across all frequencies
- */
+ /** Get the sum of all CPU usage across all frequencies */
+ @SuppressWarnings("ForLoopReplaceableByForEach")
private static int totalCpuUsage(int[] cpuUsage) {
int total = 0;
for (int i = 0; i < cpuUsage.length; i++) {
@@ -452,9 +414,7 @@
return total;
}
- /**
- * Add two CPU frequency usages together
- */
+ /** Add two CPU frequency usages together */
private static int[] sumCpuUsage(int[] a, int[] b) {
int[] summed = new int[a.length];
for (int i = 0; i < a.length; i++) {
@@ -463,9 +423,7 @@
return summed;
}
- /**
- * Puts frequencies and usage times into buckets
- */
+ /** Puts frequencies and usage times into buckets */
@VisibleForTesting
public static class FrequencyBucketCreator {
private final int mNumBuckets;
@@ -480,7 +438,7 @@
* Buckets based of a list of frequencies
*
* @param frequencies the frequencies to base buckets off
- * @param numBuckets how many buckets to create
+ * @param numBuckets how many buckets to create
*/
@VisibleForTesting
public FrequencyBucketCreator(long[] frequencies, int numBuckets) {
@@ -502,20 +460,20 @@
// Ensure that we don't have more buckets than frequencies
mLittleNumBuckets = Math.min(littleNumBuckets, mBigFrequenciesStartIndex);
- mBigNumBuckets = Math.min(
- bigNumBuckets, frequencies.length - mBigFrequenciesStartIndex);
+ mBigNumBuckets =
+ Math.min(bigNumBuckets, frequencies.length - mBigFrequenciesStartIndex);
mNumBuckets = mLittleNumBuckets + mBigNumBuckets;
// Set the size of each little and big bucket. If they have no buckets, the size is zero
- mLittleBucketSize = mLittleNumBuckets == 0 ? 0 :
- mBigFrequenciesStartIndex / mLittleNumBuckets;
- mBigBucketSize = mBigNumBuckets == 0 ? 0 :
- (frequencies.length - mBigFrequenciesStartIndex) / mBigNumBuckets;
+ mLittleBucketSize =
+ mLittleNumBuckets == 0 ? 0 : mBigFrequenciesStartIndex / mLittleNumBuckets;
+ mBigBucketSize =
+ mBigNumBuckets == 0
+ ? 0
+ : (frequencies.length - mBigFrequenciesStartIndex) / mBigNumBuckets;
}
- /**
- * Find the index where frequencies change from little core to big core
- */
+ /** Find the index where frequencies change from little core to big core */
@VisibleForTesting
public static int getBigFrequenciesStartIndex(long[] frequenciesKhz) {
for (int i = 0; i < frequenciesKhz.length - 1; i++) {
@@ -527,16 +485,14 @@
return frequenciesKhz.length;
}
- /**
- * Get the minimum frequency in each bucket
- */
+ /** Get the minimum frequency in each bucket */
@VisibleForTesting
public int[] getBucketMinFrequencies(long[] frequenciesKhz) {
Preconditions.checkArgument(frequenciesKhz.length == mNumFrequencies);
// If there's only one bucket, we bucket everything together so the first bucket is the
// min frequency
if (mNumBuckets == 1) {
- return new int[]{(int) frequenciesKhz[0]};
+ return new int[] {(int) frequenciesKhz[0]};
}
final int[] bucketMinFrequencies = new int[mNumBuckets];
@@ -561,6 +517,7 @@
* @return the bucketed usage times
*/
@VisibleForTesting
+ @SuppressWarnings("ForLoopReplaceableByForEach")
public int[] getBucketedValues(long[] values) {
Preconditions.checkArgument(values.length == mNumFrequencies);
final int[] bucketed = new int[mNumBuckets];
@@ -580,18 +537,18 @@
}
// Initialize the big buckets
for (int i = mBigFrequenciesStartIndex; i < values.length; i++) {
- final int bucketIndex = Math.min(
- mLittleNumBuckets + (i - mBigFrequenciesStartIndex) / mBigBucketSize,
- mNumBuckets - 1);
+ final int bucketIndex =
+ Math.min(
+ mLittleNumBuckets
+ + (i - mBigFrequenciesStartIndex) / mBigBucketSize,
+ mNumBuckets - 1);
bucketed[bucketIndex] += values[i];
}
return bucketed;
}
}
- /**
- * CPU usage of a process
- */
+ /** CPU usage of a process */
public static class ProcessCpuUsage {
public final int processId;
public final String processName;
@@ -610,46 +567,23 @@
}
}
- /**
- * CPU usage of a thread
- */
+ /** CPU usage of a thread */
public static class ThreadCpuUsage {
public final int threadId;
public final String threadName;
public final int[] usageTimesMillis;
- ThreadCpuUsage(
- int threadId,
- String threadName,
- int[] usageTimesMillis) {
+ ThreadCpuUsage(int threadId, String threadName, int[] usageTimesMillis) {
this.threadId = threadId;
this.threadName = threadName;
this.usageTimesMillis = usageTimesMillis;
}
}
- /**
- * Used to inject static methods from {@link Process}
- */
+ /** Used to inject static methods from {@link Process} */
@VisibleForTesting
public static class Injector {
- /**
- * Get the PID of the current process
- */
- public int myPid() {
- return Process.myPid();
- }
-
- /**
- * Get the UID that owns the current process
- */
- public int myUid() {
- return Process.myUid();
- }
-
- /**
- * Get the UID for the process with ID {@code pid}
- */
+ /** Get the UID for the process with ID {@code pid} */
public int getUidForPid(int pid) {
return Process.getUidForPid(pid);
}
diff --git a/core/java/com/android/internal/os/KernelCpuThreadReaderSettingsObserver.java b/core/java/com/android/internal/os/KernelCpuThreadReaderSettingsObserver.java
index b8dfe0d..3851ce6 100644
--- a/core/java/com/android/internal/os/KernelCpuThreadReaderSettingsObserver.java
+++ b/core/java/com/android/internal/os/KernelCpuThreadReaderSettingsObserver.java
@@ -47,33 +47,29 @@
public class KernelCpuThreadReaderSettingsObserver extends ContentObserver {
private static final String TAG = "KernelCpuThreadReaderSettingsObserver";
- /**
- * The number of frequency buckets to report
- */
+ /** The number of frequency buckets to report */
private static final String NUM_BUCKETS_SETTINGS_KEY = "num_buckets";
+
private static final int NUM_BUCKETS_DEFAULT = 8;
- /**
- * List of UIDs to report data for
- */
+ /** List of UIDs to report data for */
private static final String COLLECTED_UIDS_SETTINGS_KEY = "collected_uids";
+
private static final String COLLECTED_UIDS_DEFAULT = "0-0;1000-1000";
- /**
- * Minimum total CPU usage to report
- */
+ /** Minimum total CPU usage to report */
private static final String MINIMUM_TOTAL_CPU_USAGE_MILLIS_SETTINGS_KEY =
"minimum_total_cpu_usage_millis";
+
private static final int MINIMUM_TOTAL_CPU_USAGE_MILLIS_DEFAULT = 10000;
private final Context mContext;
- @Nullable
- private final KernelCpuThreadReader mKernelCpuThreadReader;
+ @Nullable private final KernelCpuThreadReader mKernelCpuThreadReader;
/**
- * @return returns a created {@link KernelCpuThreadReader} that will be modified by any
- * change in settings, returns null if creation failed
+ * @return returns a created {@link KernelCpuThreadReader} that will be modified by any change
+ * in settings, returns null if creation failed
*/
@Nullable
public static KernelCpuThreadReader getSettingsModifiedReader(Context context) {
@@ -81,10 +77,10 @@
KernelCpuThreadReaderSettingsObserver settingsObserver =
new KernelCpuThreadReaderSettingsObserver(context);
// Register the observer to listen for setting changes
- Uri settingsUri =
- Settings.Global.getUriFor(Settings.Global.KERNEL_CPU_THREAD_READER);
- context.getContentResolver().registerContentObserver(
- settingsUri, false, settingsObserver, UserHandle.USER_SYSTEM);
+ Uri settingsUri = Settings.Global.getUriFor(Settings.Global.KERNEL_CPU_THREAD_READER);
+ context.getContentResolver()
+ .registerContentObserver(
+ settingsUri, false, settingsObserver, UserHandle.USER_SYSTEM);
// Return the observer's reader
return settingsObserver.mKernelCpuThreadReader;
}
@@ -92,10 +88,11 @@
private KernelCpuThreadReaderSettingsObserver(Context context) {
super(BackgroundThread.getHandler());
mContext = context;
- mKernelCpuThreadReader = KernelCpuThreadReader.create(
- NUM_BUCKETS_DEFAULT,
- UidPredicate.fromString(COLLECTED_UIDS_DEFAULT),
- MINIMUM_TOTAL_CPU_USAGE_MILLIS_DEFAULT);
+ mKernelCpuThreadReader =
+ KernelCpuThreadReader.create(
+ NUM_BUCKETS_DEFAULT,
+ UidPredicate.fromString(COLLECTED_UIDS_DEFAULT),
+ MINIMUM_TOTAL_CPU_USAGE_MILLIS_DEFAULT);
}
@Override
@@ -103,9 +100,7 @@
updateReader();
}
- /**
- * Update the reader with new settings
- */
+ /** Update the reader with new settings */
private void updateReader() {
if (mKernelCpuThreadReader == null) {
return;
@@ -113,8 +108,10 @@
final KeyValueListParser parser = new KeyValueListParser(',');
try {
- parser.setString(Settings.Global.getString(
- mContext.getContentResolver(), Settings.Global.KERNEL_CPU_THREAD_READER));
+ parser.setString(
+ Settings.Global.getString(
+ mContext.getContentResolver(),
+ Settings.Global.KERNEL_CPU_THREAD_READER));
} catch (IllegalArgumentException e) {
Slog.e(TAG, "Bad settings", e);
return;
@@ -122,8 +119,9 @@
final UidPredicate uidPredicate;
try {
- uidPredicate = UidPredicate.fromString(
- parser.getString(COLLECTED_UIDS_SETTINGS_KEY, COLLECTED_UIDS_DEFAULT));
+ uidPredicate =
+ UidPredicate.fromString(
+ parser.getString(COLLECTED_UIDS_SETTINGS_KEY, COLLECTED_UIDS_DEFAULT));
} catch (NumberFormatException e) {
Slog.w(TAG, "Failed to get UID predicate", e);
return;
@@ -132,14 +130,13 @@
mKernelCpuThreadReader.setNumBuckets(
parser.getInt(NUM_BUCKETS_SETTINGS_KEY, NUM_BUCKETS_DEFAULT));
mKernelCpuThreadReader.setUidPredicate(uidPredicate);
- mKernelCpuThreadReader.setMinimumTotalCpuUsageMillis(parser.getInt(
- MINIMUM_TOTAL_CPU_USAGE_MILLIS_SETTINGS_KEY,
- MINIMUM_TOTAL_CPU_USAGE_MILLIS_DEFAULT));
+ mKernelCpuThreadReader.setMinimumTotalCpuUsageMillis(
+ parser.getInt(
+ MINIMUM_TOTAL_CPU_USAGE_MILLIS_SETTINGS_KEY,
+ MINIMUM_TOTAL_CPU_USAGE_MILLIS_DEFAULT));
}
- /**
- * Check whether a UID belongs to a set of UIDs
- */
+ /** Check whether a UID belongs to a set of UIDs */
@VisibleForTesting
public static class UidPredicate implements Predicate<Integer> {
private static final Pattern UID_RANGE_PATTERN = Pattern.compile("([0-9]+)-([0-9]+)");
@@ -150,14 +147,14 @@
* Create a UID predicate from a string representing a list of UID ranges
*
* <p>UID ranges are a pair of integers separated by a '-'. If you want to specify a single
- * UID (e.g. UID 1000), you can use {@code 1000-1000}. Lists of ranges are separated by
- * a single ';'. For example, this would be a valid string representation: {@code
+ * UID (e.g. UID 1000), you can use {@code 1000-1000}. Lists of ranges are separated by a
+ * single ';'. For example, this would be a valid string representation: {@code
* "1000-1999;2003-2003;2004-2004;2050-2060"}.
*
* <p>We do not use ',' to delimit as it is already used in separating different setting
* arguments.
*
- * @throws NumberFormatException if the input string is incorrectly formatted
+ * @throws NumberFormatException if the input string is incorrectly formatted
* @throws IllegalArgumentException if an UID range has a lower end than start
*/
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
@@ -169,9 +166,10 @@
throw new NumberFormatException(
"Failed to recognize as number range: " + uidSpecifier);
}
- acceptedUidRanges.add(Range.create(
- Integer.parseInt(uidRangeMatcher.group(1)),
- Integer.parseInt(uidRangeMatcher.group(2))));
+ acceptedUidRanges.add(
+ Range.create(
+ Integer.parseInt(uidRangeMatcher.group(1)),
+ Integer.parseInt(uidRangeMatcher.group(2))));
}
return new UidPredicate(acceptedUidRanges);
}
@@ -181,6 +179,7 @@
}
@Override
+ @SuppressWarnings("ForLoopReplaceableByForEach")
public boolean test(Integer uid) {
for (int i = 0; i < mAcceptedUidRanges.size(); i++) {
if (mAcceptedUidRanges.get(i).contains(uid)) {
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index b4be3f5..0c53fb2 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -2319,4 +2319,10 @@
// Settings > Display > Theme
DARK_UI_SETTINGS = 1698;
+
+ // Settings > global bubble settings
+ BUBBLE_SETTINGS = 1699;
+
+ // Settings > app > bubble settings
+ APP_BUBBLE_SETTINGS = 1700;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index cb8ece7..8bfa038 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4426,6 +4426,13 @@
<permission android:name="android.permission.BIND_CARRIER_MESSAGING_CLIENT_SERVICE"
android:protectionLevel="signature" />
+ <!-- Must be required by an {@link android.service.watchdog.ExplicitHealthCheckService} to
+ ensure that only the system can bind to it.
+ @hide This is not a third-party API (intended for OEMs and system apps).
+ -->
+ <permission android:name="android.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE"
+ android:protectionLevel="signature|privileged" />
+
<!-- @hide Permission that allows configuring appops.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.MANAGE_APPOPS"
diff --git a/core/tests/coretests/res/raw/com_android_tzdata.apex b/core/tests/coretests/res/raw/com_android_tzdata.apex
index 72294de..06ea8fa 100644
--- a/core/tests/coretests/res/raw/com_android_tzdata.apex
+++ b/core/tests/coretests/res/raw/com_android_tzdata.apex
Binary files differ
diff --git a/core/tests/coretests/src/android/content/pm/PackageParserTest.java b/core/tests/coretests/src/android/content/pm/PackageParserTest.java
index aa0e0cd..0798c0c 100644
--- a/core/tests/coretests/src/android/content/pm/PackageParserTest.java
+++ b/core/tests/coretests/src/android/content/pm/PackageParserTest.java
@@ -500,18 +500,30 @@
File apexFile = copyRawResourceToFile("com.android.tzdata.apex",
R.raw.com_android_tzdata);
PackageInfo pi = PackageParser.generatePackageInfoFromApex(apexFile, false);
- assertEquals("com.google.android.tzdata", pi.packageName);
assertEquals("com.google.android.tzdata", pi.applicationInfo.packageName);
- assertEquals(1, pi.getLongVersionCode());
+ assertTrue(pi.applicationInfo.enabled);
+ assertEquals(28, pi.applicationInfo.targetSdkVersion);
assertEquals(1, pi.applicationInfo.longVersionCode);
+
+ assertEquals("com.google.android.tzdata", pi.packageName);
+ assertTrue(pi.splitNames.length > 0);
+ assertEquals(1, pi.getLongVersionCode());
assertNull(pi.signingInfo);
+ assertNull(pi.signatures);
+ assertTrue(pi.isApex);
pi = PackageParser.generatePackageInfoFromApex(apexFile, true);
- assertEquals("com.google.android.tzdata", pi.packageName);
assertEquals("com.google.android.tzdata", pi.applicationInfo.packageName);
- assertEquals(1, pi.getLongVersionCode());
+ assertTrue(pi.applicationInfo.enabled);
+ assertEquals(28, pi.applicationInfo.targetSdkVersion);
assertEquals(1, pi.applicationInfo.longVersionCode);
+
+ assertEquals("com.google.android.tzdata", pi.packageName);
+ assertTrue(pi.splitNames.length > 0);
+ assertEquals(1, pi.getLongVersionCode());
assertNotNull(pi.signingInfo);
+ assertNotNull(pi.signatures);
assertTrue(pi.signingInfo.getApkContentsSigners().length > 0);
+ assertTrue(pi.isApex);
}
}
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
index 1cb71f9..1a22a70 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
@@ -45,7 +45,6 @@
import java.util.Arrays;
import java.util.List;
-import java.util.concurrent.atomic.AtomicInteger;
@LargeTest
@RunWith(AndroidJUnit4.class)
@@ -61,16 +60,12 @@
AccessibilityCache mAccessibilityCache;
AccessibilityCache.AccessibilityNodeRefresher mAccessibilityNodeRefresher;
- AtomicInteger mNumA11yNodeInfosInUse = new AtomicInteger(0);
- AtomicInteger mNumA11yWinInfosInUse = new AtomicInteger(0);
@Before
public void setUp() {
mAccessibilityNodeRefresher = mock(AccessibilityCache.AccessibilityNodeRefresher.class);
when(mAccessibilityNodeRefresher.refreshNode(anyObject(), anyBoolean())).thenReturn(true);
mAccessibilityCache = new AccessibilityCache(mAccessibilityNodeRefresher);
- AccessibilityNodeInfo.setNumInstancesInUseCounter(mNumA11yNodeInfosInUse);
- AccessibilityWindowInfo.setNumInstancesInUseCounter(mNumA11yWinInfosInUse);
}
@After
@@ -78,8 +73,6 @@
// Make sure we're recycling all of our window and node infos
mAccessibilityCache.clear();
AccessibilityInteractionClient.getInstance().clearCache();
- assertEquals(0, mNumA11yWinInfosInUse.get());
- assertEquals(0, mNumA11yNodeInfosInUse.get());
}
@Test
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderEndToEndTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderEndToEndTest.java
index 1c84829..e9cad0a 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderEndToEndTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderEndToEndTest.java
@@ -35,6 +35,7 @@
import java.time.Duration;
import java.util.Arrays;
import java.util.List;
+import java.util.Optional;
import java.util.OptionalDouble;
import java.util.concurrent.CountDownLatch;
import java.util.function.Consumer;
@@ -126,17 +127,21 @@
final KernelCpuThreadReader kernelCpuThreadReader =
KernelCpuThreadReader.create(8, uid -> uid == Process.myUid(), 0);
assertNotNull(kernelCpuThreadReader);
- final ProcessCpuUsage currentProcessCpuUsage =
- kernelCpuThreadReader.getCurrentProcessCpuUsage();
+ kernelCpuThreadReader.setUidPredicate(uid -> uid == Process.myUid());
+ final Optional<ProcessCpuUsage> currentProcessCpuUsage =
+ kernelCpuThreadReader.getProcessCpuUsage().stream()
+ .filter(p -> p.processId == Process.myPid())
+ .findFirst();
+ assertTrue(currentProcessCpuUsage.isPresent());
// Threads can terminate, as we've finished crawling them from /proc
threadFinishedLatch.countDown();
// Check that we've got times for every thread we spawned
- final List<ThreadCpuUsage> threadCpuUsages = currentProcessCpuUsage.threadCpuUsages
- .stream()
- .filter((thread) -> thread.threadName.startsWith(tag))
- .collect(Collectors.toList());
+ final List<ThreadCpuUsage> threadCpuUsages =
+ currentProcessCpuUsage.get().threadCpuUsages.stream()
+ .filter((thread) -> thread.threadName.startsWith(tag))
+ .collect(Collectors.toList());
assertEquals(
"Incorrect number of threads returned by KernelCpuThreadReader",
numSamples, threadCpuUsages.size());
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
index e6e7a85..61209e2 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
@@ -25,6 +25,7 @@
import android.content.Context;
import android.os.FileUtils;
+import android.platform.test.annotations.Presubmit;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -44,27 +45,10 @@
import java.util.Comparator;
import java.util.function.Predicate;
+@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
public class KernelCpuThreadReaderTest {
-
- private static final int UID = 1000;
- private static final int PROCESS_ID = 1234;
- private static final int[] THREAD_IDS = {0, 1000, 1235, 4321};
- private static final String PROCESS_NAME = "test_process";
- private static final String[] THREAD_NAMES = {
- "test_thread_1", "test_thread_2", "test_thread_3", "test_thread_4"
- };
- private static final int[] THREAD_CPU_FREQUENCIES = {
- 1000, 2000, 3000, 4000,
- };
- private static final int[][] THREAD_CPU_TIMES = {
- {1, 0, 0, 1},
- {0, 0, 0, 0},
- {1000, 1000, 1000, 1000},
- {0, 1, 2, 3},
- };
-
private File mProcDirectory;
@Before
@@ -79,41 +63,6 @@
}
@Test
- public void testReader_currentProcess() throws IOException {
- KernelCpuThreadReader.Injector processUtils =
- new KernelCpuThreadReader.Injector() {
- @Override
- public int myPid() {
- return PROCESS_ID;
- }
-
- @Override
- public int myUid() {
- return UID;
- }
-
- @Override
- public int getUidForPid(int pid) {
- return 0;
- }
- };
- setupDirectory(mProcDirectory.toPath().resolve("self"), THREAD_IDS, PROCESS_NAME,
- THREAD_NAMES, THREAD_CPU_FREQUENCIES, THREAD_CPU_TIMES);
-
- final KernelCpuThreadReader kernelCpuThreadReader = new KernelCpuThreadReader(
- 8,
- uid -> 1000 <= uid && uid < 2000,
- 0,
- mProcDirectory.toPath(),
- mProcDirectory.toPath().resolve("self/task/" + THREAD_IDS[0] + "/time_in_state"),
- processUtils);
- final KernelCpuThreadReader.ProcessCpuUsage processCpuUsage =
- kernelCpuThreadReader.getCurrentProcessCpuUsage();
- checkResults(processCpuUsage, kernelCpuThreadReader.getCpuFrequenciesKhz(), UID, PROCESS_ID,
- THREAD_IDS, PROCESS_NAME, THREAD_NAMES, THREAD_CPU_FREQUENCIES, THREAD_CPU_TIMES);
- }
-
- @Test
public void testReader_byUids() throws IOException {
int[] uids = new int[]{0, 2, 3, 4, 5, 6000};
Predicate<Integer> uidPredicate = uid -> uid == 0 || uid >= 4;
@@ -121,16 +70,6 @@
KernelCpuThreadReader.Injector processUtils =
new KernelCpuThreadReader.Injector() {
@Override
- public int myPid() {
- return 0;
- }
-
- @Override
- public int myUid() {
- return 0;
- }
-
- @Override
public int getUidForPid(int pid) {
return pid;
}
@@ -150,7 +89,7 @@
mProcDirectory.toPath().resolve(uids[0] + "/task/" + uids[0] + "/time_in_state"),
processUtils);
ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processCpuUsageByUids =
- kernelCpuThreadReader.getProcessCpuUsageByUids();
+ kernelCpuThreadReader.getProcessCpuUsage();
processCpuUsageByUids.sort(Comparator.comparing(usage -> usage.processId));
assertEquals(expectedUids.length, processCpuUsageByUids.size());
@@ -173,16 +112,6 @@
KernelCpuThreadReader.Injector processUtils =
new KernelCpuThreadReader.Injector() {
@Override
- public int myPid() {
- return 0;
- }
-
- @Override
- public int myUid() {
- return 0;
- }
-
- @Override
public int getUidForPid(int pid) {
return pid;
}
@@ -206,7 +135,7 @@
mProcDirectory.toPath().resolve(uids[0] + "/task/" + uids[0] + "/time_in_state"),
processUtils);
ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processCpuUsageByUids =
- kernelCpuThreadReader.getProcessCpuUsageByUids();
+ kernelCpuThreadReader.getProcessCpuUsage();
processCpuUsageByUids.sort(Comparator.comparing(usage -> usage.uid));
assertEquals(expectedUids.length, processCpuUsageByUids.size());
@@ -220,7 +149,7 @@
@Test
public void testReader_otherThreads() throws IOException {
- final Path processPath = mProcDirectory.toPath().resolve("self");
+ final Path processPath = mProcDirectory.toPath().resolve("1000");
setupDirectory(
processPath,
new int[]{1, 2, 3},
@@ -228,39 +157,34 @@
new String[]{"thread1", "thread2", "thread3"},
new int[]{1000, 2000},
new int[][]{{0, 100}, {10, 0}, {0, 300}});
- final KernelCpuThreadReader kernelCpuThreadReader = new KernelCpuThreadReader(
- 8,
- i -> true,
- 2000,
- mProcDirectory.toPath(),
- processPath.resolve("task/1/time_in_state"),
+ KernelCpuThreadReader.Injector injector =
new KernelCpuThreadReader.Injector() {
@Override
- public int myPid() {
- return 1000;
- }
-
- @Override
- public int myUid() {
- return 0;
- }
-
- @Override
public int getUidForPid(int pid) {
return 0;
}
- });
+ };
+ final KernelCpuThreadReader kernelCpuThreadReader =
+ new KernelCpuThreadReader(
+ 8,
+ uid -> true,
+ 2000,
+ mProcDirectory.toPath(),
+ processPath.resolve("task/1/time_in_state"),
+ injector);
+ ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processCpuUsages =
+ kernelCpuThreadReader.getProcessCpuUsage();
+ assertEquals(1, processCpuUsages.size());
checkResults(
- kernelCpuThreadReader.getCurrentProcessCpuUsage(),
+ processCpuUsages.get(0),
kernelCpuThreadReader.getCpuFrequenciesKhz(),
0,
1000,
- new int[]{-1, 3},
+ new int[] {-1, 3},
"process",
- new String[]{"__OTHER_THREADS", "thread3"},
- new int[]{1000, 2000},
- new int[][]{{100, 1000}, {0, 3000}}
- );
+ new String[] {"__OTHER_THREADS", "thread3"},
+ new int[] {1000, 2000},
+ new int[][] {{10, 100}, {0, 300}});
}
private void setupDirectory(Path processPath, int[] threadIds, String processName,
@@ -289,8 +213,7 @@
final OutputStream timeInStateStream =
Files.newOutputStream(threadPath.resolve("time_in_state"));
for (int j = 0; j < cpuFrequencies.length; j++) {
- final String line = String.valueOf(cpuFrequencies[j]) + " "
- + String.valueOf(cpuTimes[i][j]) + "\n";
+ final String line = cpuFrequencies[j] + " " + cpuTimes[i][j] + "\n";
timeInStateStream.write(line.getBytes());
}
timeInStateStream.close();
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index bb47658..3968f84 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -66,6 +66,14 @@
}
prebuilt_etc {
+ name: "privapp_whitelist_com.android.emergency",
+ product_specific: true,
+ sub_dir: "permissions",
+ src: "com.android.emergency.xml",
+ filename_from_src: true,
+}
+
+prebuilt_etc {
name: "privapp_whitelist_com.android.launcher3",
product_specific: true,
sub_dir: "permissions",
diff --git a/data/etc/com.android.emergency.xml b/data/etc/com.android.emergency.xml
new file mode 100644
index 0000000..28f99dd
--- /dev/null
+++ b/data/etc/com.android.emergency.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<permissions>
+ <privapp-permissions package="com.android.emergency">
+ <!-- Required to place emergency calls from emergency info screen. -->
+ <permission name="android.permission.CALL_PRIVILEGED"/>
+ <permission name="android.permission.MANAGE_USERS"/>
+ </privapp-permissions>
+</permissions>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index afb5071..347edc5 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -50,12 +50,6 @@
<permission name="android.permission.WRITE_MEDIA_STORAGE"/>
</privapp-permissions>
- <privapp-permissions package="com.android.emergency">
- <!-- Required to place emergency calls from emergency info screen. -->
- <permission name="android.permission.CALL_PRIVILEGED"/>
- <permission name="android.permission.MANAGE_USERS"/>
- </privapp-permissions>
-
<privapp-permissions package="com.android.externalstorage">
<permission name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<permission name="android.permission.WRITE_MEDIA_STORAGE"/>
@@ -124,6 +118,7 @@
<permission name="android.permission.REQUEST_INCIDENT_REPORT_APPROVAL"/>
<permission name="android.permission.APPROVE_INCIDENT_REPORTS"/>
<permission name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
+ <permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME" />
</privapp-permissions>
<privapp-permissions package="com.android.phone">
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 4bd344f..170dec2 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -1813,21 +1813,22 @@
}
/**
- * Fills the bitmap's pixels with the specified {@link Color}.
+ * Fills the bitmap's pixels with the specified {@code ColorLong}.
*
+ * @param color The color to fill as packed by the {@link Color} class.
* @throws IllegalStateException if the bitmap is not mutable.
- * @throws IllegalArgumentException if the color space encoded in the long
- * is invalid or unknown.
+ * @throws IllegalArgumentException if the color space encoded in the
+ * {@code ColorLong} is invalid or unknown.
*
*/
- public void eraseColor(@ColorLong long c) {
+ public void eraseColor(@ColorLong long color) {
checkRecycled("Can't erase a recycled bitmap");
if (!isMutable()) {
throw new IllegalStateException("cannot erase immutable bitmaps");
}
- ColorSpace cs = Color.colorSpace(c);
- nativeErase(mNativePtr, cs.getNativeInstance(), c);
+ ColorSpace cs = Color.colorSpace(color);
+ nativeErase(mNativePtr, cs.getNativeInstance(), color);
}
/**
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 7b3f3da..df64204 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -1683,8 +1683,9 @@
* Fill the entire canvas' bitmap (restricted to the current clip) with the specified color,
* using srcover porterduff mode.
*
- * @param color the color to draw onto the canvas
- * @throws IllegalArgumentException if the color space encoded in the long
+ * @param color the {@code ColorLong} to draw onto the canvas. See the {@link Color}
+ * class for details about {@code ColorLong}s.
+ * @throws IllegalArgumentException if the color space encoded in the {@code ColorLong}
* is invalid or unknown.
*/
public void drawColor(@ColorLong long color) {
@@ -1695,7 +1696,7 @@
* Fill the entire canvas' bitmap (restricted to the current clip) with the specified color and
* porter-duff xfermode.
*
- * @param color the color to draw with
+ * @param color the color to draw onto the canvas
* @param mode the porter-duff mode to apply to the color
*
* @deprecated use {@link #drawColor(int, BlendMode)} instead
@@ -1709,7 +1710,7 @@
* Fill the entire canvas' bitmap (restricted to the current clip) with the specified color and
* blendmode.
*
- * @param color the color to draw with
+ * @param color the color to draw onto the canvas
* @param mode the blendmode to apply to the color
*/
public void drawColor(@ColorInt int color, @NonNull BlendMode mode) {
@@ -1720,9 +1721,10 @@
* Fill the entire canvas' bitmap (restricted to the current clip) with the specified color and
* blendmode.
*
- * @param color the color to draw with
+ * @param color the {@code ColorLong} to draw onto the canvas. See the {@link Color}
+ * class for details about {@code ColorLong}s.
* @param mode the blendmode to apply to the color
- * @throws IllegalArgumentException if the color space encoded in the long
+ * @throws IllegalArgumentException if the color space encoded in the {@code ColorLong}
* is invalid or unknown.
*/
public void drawColor(@ColorLong long color, @NonNull BlendMode mode) {
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index c485461..db5f065 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -1029,9 +1029,8 @@
* premultiplied, meaning that alpha can be any value, regardless of the
* values of r,g,b. See the {@link Color} class for more details.
*
- * @see Color for APIs that help manipulate a color long.
- *
- * @return the paint's color (and alpha).
+ * @return the paint's color, alpha, and {@code ColorSpace} encoded as a
+ * {@code ColorLong}
*/
@ColorLong
public long getColorLong() {
@@ -1052,7 +1051,7 @@
}
/**
- * Set the paint's color with a {@link ColorLong}. Note that the color is
+ * Set the paint's color with a {@code ColorLong}. Note that the color is
* a long with an encoded {@link ColorSpace} as well as alpha and r,g,b.
* These values are not premultiplied, meaning that alpha can be any value,
* regardless of the values of r,g,b. See the {@link Color} class for more
@@ -1060,8 +1059,8 @@
*
* @param color The new color (including alpha and {@link ColorSpace})
* to set in the paint.
- * @throws IllegalArgumentException if the color space encoded in the long
- * is invalid or unknown.
+ * @throws IllegalArgumentException if the color space encoded in the
+ * {@code ColorLong} is invalid or unknown.
*/
public void setColor(@ColorLong long color) {
ColorSpace cs = Color.colorSpace(color);
@@ -1491,8 +1490,8 @@
* The alpha of the shadow will be the paint's alpha if the shadow color is
* opaque, or the alpha from the shadow color if not.
*
- * @throws IllegalArgumentException if the color space encoded in the long
- * is invalid or unknown.
+ * @throws IllegalArgumentException if the color space encoded in the
+ * {@code ColorLong} is invalid or unknown.
*/
public void setShadowLayer(float radius, float dx, float dy, @ColorLong long shadowColor) {
ColorSpace cs = Color.colorSpace(shadowColor);
@@ -1559,8 +1558,11 @@
/**
* Returns the color of the shadow layer.
+ *
+ * @return the shadow layer's color encoded as a {@link ColorLong}.
* @see #setShadowLayer(float,float,float,int)
* @see #setShadowLayer(float,float,float,long)
+ * @see Color
*/
public @ColorLong long getShadowLayerColorLong() {
return mShadowLayerColor;
diff --git a/media/java/android/media/IMediaRoute2Callback.aidl b/media/java/android/media/IMediaRoute2Callback.aidl
new file mode 100644
index 0000000..f03c8ab
--- /dev/null
+++ b/media/java/android/media/IMediaRoute2Callback.aidl
@@ -0,0 +1,24 @@
+/*
+ * 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;
+
+/**
+ * @hide
+ */
+oneway interface IMediaRoute2Callback {
+ void onRouteSelected(int uid, String routeId);
+}
diff --git a/media/java/android/media/IMediaRoute2Provider.aidl b/media/java/android/media/IMediaRoute2Provider.aidl
new file mode 100644
index 0000000..b97dcc5
--- /dev/null
+++ b/media/java/android/media/IMediaRoute2Provider.aidl
@@ -0,0 +1,27 @@
+/*
+ * 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;
+
+import android.media.IMediaRoute2Callback;
+
+/**
+ * {@hide}
+ */
+oneway interface IMediaRoute2Provider {
+ void setCallback(IMediaRoute2Callback callback);
+ void selectRoute(int uid, String id);
+}
diff --git a/media/java/android/media/IMediaRouter2ManagerClient.aidl b/media/java/android/media/IMediaRouter2ManagerClient.aidl
new file mode 100644
index 0000000..234551b
--- /dev/null
+++ b/media/java/android/media/IMediaRouter2ManagerClient.aidl
@@ -0,0 +1,25 @@
+/*
+ * 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;
+
+/**
+ * {@hide}
+ */
+oneway interface IMediaRouter2ManagerClient {
+ void onRouteSelected(int uid, String routeId);
+ void onControlCategoriesChanged(int uid, in List<String> categories);
+}
diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl
index 3308fc9..59f1d0d 100644
--- a/media/java/android/media/IMediaRouterService.aidl
+++ b/media/java/android/media/IMediaRouterService.aidl
@@ -17,6 +17,7 @@
package android.media;
import android.media.IMediaRouterClient;
+import android.media.IMediaRouter2ManagerClient;
import android.media.MediaRouterClientState;
/**
@@ -29,8 +30,15 @@
MediaRouterClientState getState(IMediaRouterClient client);
boolean isPlaybackActive(IMediaRouterClient client);
+ void setControlCategories(IMediaRouterClient client, in List<String> categories);
void setDiscoveryRequest(IMediaRouterClient client, int routeTypes, boolean activeScan);
void setSelectedRoute(IMediaRouterClient client, String routeId, boolean explicit);
void requestSetVolume(IMediaRouterClient client, String routeId, int volume);
void requestUpdateVolume(IMediaRouterClient client, String routeId, int direction);
+
+ void registerManagerAsUser(IMediaRouter2ManagerClient callback,
+ String packageName, int userId);
+ void unregisterManager(IMediaRouter2ManagerClient callback);
+ void setRemoteRoute(IMediaRouter2ManagerClient callback,
+ int uid, String routeId, boolean explicit);
}
diff --git a/media/java/android/media/MediaRoute2ProviderService.java b/media/java/android/media/MediaRoute2ProviderService.java
new file mode 100644
index 0000000..04ddc30
--- /dev/null
+++ b/media/java/android/media/MediaRoute2ProviderService.java
@@ -0,0 +1,108 @@
+/*
+ * 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;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public abstract class MediaRoute2ProviderService extends Service {
+ private static final String TAG = "MediaRouteProviderSrv";
+
+ public static final String SERVICE_INTERFACE = "android.media.MediaRoute2ProviderService";
+
+ private final Handler mHandler;
+ private ProviderStub mStub;
+ private IMediaRoute2Callback mCallback;
+
+ public MediaRoute2ProviderService() {
+ mHandler = new Handler(Looper.getMainLooper());
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ if (SERVICE_INTERFACE.equals(intent.getAction())) {
+ if (mStub == null) {
+ mStub = new ProviderStub();
+ }
+ return mStub;
+ }
+ return null;
+ }
+
+ /**
+ * Called when selectRoute is called on a route of the provider.
+ *
+ * @param uid The target application uid
+ * @param routeId The id of the target route
+ */
+ public abstract void onSelect(int uid, String routeId);
+
+ /**
+ * Updates provider info from selected route and appliation.
+ *
+ * TODO: When provider descriptor is defined, this should update the descriptor correctly.
+ *
+ * @param uid
+ * @param routeId
+ */
+ public void updateProvider(int uid, String routeId) {
+ if (mCallback != null) {
+ try {
+ //TODO: After publishState() is fully implemented, delete this.
+ mCallback.onRouteSelected(uid, routeId);
+ } catch (RemoteException ex) {
+ Log.d(TAG, "Failed to update provider");
+ }
+ }
+ publishState();
+ }
+
+ void setCallback(IMediaRoute2Callback callback) {
+ mCallback = callback;
+ publishState();
+ }
+
+ void publishState() {
+ //TODO: Send provider descriptor to the MediaRouterService
+ }
+
+ final class ProviderStub extends IMediaRoute2Provider.Stub {
+ ProviderStub() { }
+
+ @Override
+ public void setCallback(IMediaRoute2Callback callback) {
+ mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::setCallback,
+ MediaRoute2ProviderService.this, callback));
+ }
+
+ @Override
+ public void selectRoute(int uid, String id) {
+ mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onSelect,
+ MediaRoute2ProviderService.this, uid, id));
+ }
+ }
+}
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index 3444e92..5a89d8c 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -347,6 +347,17 @@
return mDisplayService.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION);
}
+ void setControlCategories(List<String> categories) {
+ if (mClient != null) {
+ try {
+ mMediaRouterService.setControlCategories(mClient,
+ categories);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Unable to set control categories.", ex);
+ }
+ }
+ }
+
private void updatePresentationDisplays(int changedDisplayId) {
final int count = mRoutes.size();
for (int i = 0; i < count; i++) {
@@ -919,6 +930,25 @@
return -1;
}
+ //TODO: Remove @hide when it is ready.
+ //TODO: Provide pre-defined categories for app developers.
+ /**
+ * Sets control categories of the client application.
+ * Control categories can be used to filter out media routes
+ * that don't correspond with the client application.
+ * The only routes that match any of the categories will be shown on other applications.
+ *
+ * @hide
+ * @param categories Categories to set
+ */
+ public void setControlCategories(@NonNull List<String> categories) {
+ if (categories == null) {
+ throw new IllegalArgumentException("Categories must not be null");
+ }
+ sStatic.setControlCategories(categories);
+ }
+
+
/**
* Select the specified route to use for output of the given media types.
* <p class="note">
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
new file mode 100644
index 0000000..ac5958e
--- /dev/null
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -0,0 +1,241 @@
+/*
+ * 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;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.content.Context;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * @hide
+ */
+public class MediaRouter2Manager {
+ private static final String TAG = "MediaRouter2Manager";
+ private static final Object sLock = new Object();
+
+ @GuardedBy("sLock")
+ private static MediaRouter2Manager sInstance;
+
+ final String mPackageName;
+
+ private Context mContext;
+ private Client mClient;
+ private final IMediaRouterService mMediaRouterService;
+ final Handler mHandler;
+
+ @GuardedBy("sLock")
+ final ArrayList<CallbackRecord> mCallbacks = new ArrayList<>();
+
+ /**
+ * Gets an instance of media router manager that controls media route of other apps.
+ * @param context
+ * @return
+ */
+ public static MediaRouter2Manager getInstance(@NonNull Context context) {
+ if (context == null) {
+ throw new IllegalArgumentException("context must not be null");
+ }
+ synchronized (sLock) {
+ if (sInstance == null) {
+ sInstance = new MediaRouter2Manager(context);
+ }
+ return sInstance;
+ }
+ }
+
+ private MediaRouter2Manager(Context context) {
+ mContext = context.getApplicationContext();
+ mMediaRouterService = IMediaRouterService.Stub.asInterface(
+ ServiceManager.getService(Context.MEDIA_ROUTER_SERVICE));
+ mPackageName = mContext.getPackageName();
+ mHandler = new Handler(context.getMainLooper());
+ }
+
+ /**
+ * Registers a callback to listen route info.
+ *
+ * @param executor The executor that runs the callback.
+ * @param callback The callback to add.
+ */
+ public void addCallback(@NonNull @CallbackExecutor Executor executor,
+ @NonNull Callback callback) {
+
+ if (executor == null) {
+ throw new IllegalArgumentException("executor must not be null");
+ }
+ if (callback == null) {
+ throw new IllegalArgumentException("callback must not be null");
+ }
+
+ synchronized (sLock) {
+ final int index = findCallbackRecord(callback);
+ if (index >= 0) {
+ Log.w(TAG, "Ignore adding the same callback twice.");
+ return;
+ }
+ if (mCallbacks.size() == 0) {
+ Client client = new Client();
+ try {
+ mMediaRouterService.registerManagerAsUser(client, mPackageName,
+ UserHandle.myUserId());
+ mClient = client;
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Unable to register media router manager.", ex);
+ }
+ }
+ mCallbacks.add(new CallbackRecord(executor, callback));
+ }
+ }
+
+ /**
+ * Removes the specified callback.
+ *
+ * @param callback The callback to remove.
+ */
+ public void removeCallback(@NonNull Callback callback) {
+ if (callback == null) {
+ throw new IllegalArgumentException("callback must not be null");
+ }
+
+ synchronized (sLock) {
+ final int index = findCallbackRecord(callback);
+ if (index < 0) {
+ Log.w(TAG, "Ignore removing unknown callback. " + callback);
+ return;
+ }
+ mCallbacks.remove(index);
+ if (mCallbacks.size() == 0 && mClient != null) {
+ try {
+ mMediaRouterService.unregisterManager(mClient);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Unable to unregister media router manager", ex);
+ }
+ mClient = null;
+ }
+ }
+ }
+
+ private int findCallbackRecord(Callback callback) {
+ final int count = mCallbacks.size();
+ for (int i = 0; i < count; i++) {
+ if (mCallbacks.get(i).mCallback == callback) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Selects media route for the specified application uid.
+ *
+ * @param uid The uid of the application that should change it's media route.
+ * @param routeId The id of the route to select
+ */
+ public void selectRoute(int uid, String routeId) {
+ if (mClient != null) {
+ try {
+ mMediaRouterService.setRemoteRoute(mClient, uid, routeId, /* explicit= */true);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Unable to select media route", ex);
+ }
+ }
+ }
+
+ /**
+ * Unselects media route for the specified application uid.
+ *
+ * @param uid The uid of the application that should stop routing.
+ */
+ public void unselectRoute(int uid) {
+ if (mClient != null) {
+ try {
+ mMediaRouterService.setRemoteRoute(mClient, uid, null, /* explicit= */ true);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Unable to select media route", ex);
+ }
+ }
+ }
+
+ void notifyRouteSelected(int uid, String routeId) {
+ for (CallbackRecord record : mCallbacks) {
+ record.mExecutor.execute(() -> record.mCallback.onRouteSelected(uid, routeId));
+ }
+ }
+
+ void notifyControlCategoriesChanged(int uid, List<String> categories) {
+ for (CallbackRecord record : mCallbacks) {
+ record.mExecutor.execute(
+ () -> record.mCallback.onControlCategoriesChanged(uid, categories));
+ }
+ }
+
+ /**
+ * Interface for receiving events about media routing changes.
+ */
+ public abstract static class Callback {
+ /**
+ * Called when a route is selected for some application uid.
+ * @param uid
+ * @param routeId
+ */
+ public abstract void onRouteSelected(int uid, String routeId);
+
+ /**
+ * Called when the control categories of an application is changed.
+ * @param uid the uid of the app that changed control categories
+ * @param categories the changed categories
+ */
+ public abstract void onControlCategoriesChanged(int uid, List<String> categories);
+ }
+
+ final class CallbackRecord {
+ public final Executor mExecutor;
+ public final Callback mCallback;
+
+ CallbackRecord(Executor executor, Callback callback) {
+ mExecutor = executor;
+ mCallback = callback;
+ }
+ }
+
+ class Client extends IMediaRouter2ManagerClient.Stub {
+ @Override
+ public void onRouteSelected(int uid, String routeId) {
+ mHandler.sendMessage(obtainMessage(MediaRouter2Manager::notifyRouteSelected,
+ MediaRouter2Manager.this, uid, routeId));
+ }
+
+ @Override
+ public void onControlCategoriesChanged(int uid, List<String> categories) {
+ mHandler.sendMessage(obtainMessage(MediaRouter2Manager::notifyControlCategoriesChanged,
+ MediaRouter2Manager.this, uid, categories));
+ }
+ }
+}
diff --git a/media/tests/MediaRouteProvider/Android.bp b/media/tests/MediaRouteProvider/Android.bp
new file mode 100644
index 0000000..da42824
--- /dev/null
+++ b/media/tests/MediaRouteProvider/Android.bp
@@ -0,0 +1,18 @@
+android_test {
+ name: "mediarouteprovider",
+
+ srcs: ["**/*.java"],
+
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ ],
+
+ static_libs: [
+ "android-support-test",
+ "mockito-target-minus-junit4",
+ ],
+
+ platform_apis: true,
+ certificate: "platform",
+}
\ No newline at end of file
diff --git a/media/tests/MediaRouteProvider/AndroidManifest.xml b/media/tests/MediaRouteProvider/AndroidManifest.xml
new file mode 100644
index 0000000..489a621
--- /dev/null
+++ b/media/tests/MediaRouteProvider/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.mediarouteprovider.example">
+
+ <application android:label="@string/app_name">
+ <uses-library android:name="android.test.runner" />
+ <service android:name=".SampleMediaRoute2ProviderService"
+ android:label="@string/app_name"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.media.MediaRoute2ProviderService" />
+ </intent-filter>
+ </service>
+ </application>
+</manifest>
diff --git a/media/tests/MediaRouteProvider/res/values/strings.xml b/media/tests/MediaRouteProvider/res/values/strings.xml
new file mode 100644
index 0000000..bb97064
--- /dev/null
+++ b/media/tests/MediaRouteProvider/res/values/strings.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <!-- name of the app [CHAR LIMIT=25]-->
+ <string name="app_name">SampleMediaRouteProvider</string>
+</resources>
\ No newline at end of file
diff --git a/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java b/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java
new file mode 100644
index 0000000..22fbd85
--- /dev/null
+++ b/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mediarouteprovider.example;
+
+import android.content.Intent;
+import android.media.MediaRoute2ProviderService;
+import android.os.IBinder;
+
+public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService {
+ @Override
+ public IBinder onBind(Intent intent) {
+ return super.onBind(intent);
+ }
+
+ @Override
+ public void onSelect(int uid, String routeId) {
+ updateProvider(uid, routeId);
+ }
+}
diff --git a/media/tests/MediaRouter/Android.bp b/media/tests/MediaRouter/Android.bp
new file mode 100644
index 0000000..611b25a
--- /dev/null
+++ b/media/tests/MediaRouter/Android.bp
@@ -0,0 +1,18 @@
+android_test {
+ name: "mediaroutertest",
+
+ srcs: ["**/*.java"],
+
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ ],
+
+ static_libs: [
+ "android-support-test",
+ "mockito-target-minus-junit4",
+ ],
+
+ platform_apis: true,
+ certificate: "platform",
+}
\ No newline at end of file
diff --git a/media/tests/MediaRouter/AndroidManifest.xml b/media/tests/MediaRouter/AndroidManifest.xml
new file mode 100644
index 0000000..a34a264
--- /dev/null
+++ b/media/tests/MediaRouter/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.mediaroutertest">
+
+ <uses-permission android:name="android.permission.CONTROL_MEDIA_ROUTE" />
+
+ <application android:label="@string/app_name">
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.mediaroutertest"
+ android:label="MediaRouter Tests"/>
+</manifest>
diff --git a/media/tests/MediaRouter/AndroidTest.xml b/media/tests/MediaRouter/AndroidTest.xml
new file mode 100644
index 0000000..1301062
--- /dev/null
+++ b/media/tests/MediaRouter/AndroidTest.xml
@@ -0,0 +1,16 @@
+<configuration description="Runs sample instrumentation test.">
+ <target_preparer class="com.android.tradefed.targetprep.TestFilePushSetup"/>
+ <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="test-file-name" value="mediaroutertest.apk"/>
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"/>
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"/>
+ <option name="test-suite-tag" value="apct"/>
+ <option name="test-tag" value="MediaRouterTest"/>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="com.android.mediaroutertest"/>
+ <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+</configuration>
diff --git a/media/tests/MediaRouter/res/values/strings.xml b/media/tests/MediaRouter/res/values/strings.xml
new file mode 100644
index 0000000..0737020
--- /dev/null
+++ b/media/tests/MediaRouter/res/values/strings.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <!-- name of the app [CHAR LIMIT=25]-->
+ <string name="app_name">mediaRouterTest</string>
+</resources>
\ No newline at end of file
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
new file mode 100644
index 0000000..a4bde65
--- /dev/null
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mediaroutertest;
+
+import static org.mockito.Mockito.after;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.media.MediaRouter;
+import android.media.MediaRouter2Manager;
+import android.support.test.InstrumentationRegistry;
+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 java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class MediaRouterManagerTest {
+ private static final String TAG = "MediaRouterManagerTest";
+
+ private static final int TARGET_UID = 109992;
+ private static final String ROUTE_1 = "MediaRoute1";
+
+ private static final int AWAIT_MS = 1000;
+ private static final int TIMEOUT_MS = 1000;
+
+ private Context mContext;
+ private MediaRouter2Manager mManager;
+ private MediaRouter mRouter;
+ private Executor mExecutor;
+
+ private static final List<String> TEST_CONTROL_CATEGORIES = new ArrayList();
+ private static final String CONTROL_CATEGORY_1 = "android.media.mediarouter.MEDIA1";
+ private static final String CONTROL_CATEGORY_2 = "android.media.mediarouter.MEDIA2";
+ static {
+ TEST_CONTROL_CATEGORIES.add(CONTROL_CATEGORY_1);
+ TEST_CONTROL_CATEGORIES.add(CONTROL_CATEGORY_2);
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getTargetContext();
+ mManager = MediaRouter2Manager.getInstance(mContext);
+ mRouter = (MediaRouter) mContext.getSystemService(Context.MEDIA_ROUTER_SERVICE);
+ mExecutor = new ThreadPoolExecutor(
+ 1, 20, 3, TimeUnit.SECONDS,
+ new SynchronousQueue<Runnable>());
+ }
+
+ @Test
+ public void transferTest() throws Exception {
+ MediaRouter2Manager.Callback mockCallback = mock(MediaRouter2Manager.Callback.class);
+
+ mManager.addCallback(mExecutor, mockCallback);
+
+ verify(mockCallback, after(AWAIT_MS).never())
+ .onRouteSelected(eq(TARGET_UID), any(String.class));
+
+ mManager.selectRoute(TARGET_UID, ROUTE_1);
+ verify(mockCallback, timeout(TIMEOUT_MS)).onRouteSelected(TARGET_UID, ROUTE_1);
+
+ mManager.removeCallback(mockCallback);
+ }
+
+ @Test
+ public void controlCategoryTest() throws Exception {
+ final int uid = android.os.Process.myUid();
+
+ MediaRouter2Manager.Callback mockCallback = mock(MediaRouter2Manager.Callback.class);
+ mManager.addCallback(mExecutor, mockCallback);
+
+ verify(mockCallback, after(AWAIT_MS).never()).onControlCategoriesChanged(eq(uid),
+ any(List.class));
+
+ mRouter.setControlCategories(TEST_CONTROL_CATEGORIES);
+ verify(mockCallback, timeout(TIMEOUT_MS).atLeastOnce())
+ .onControlCategoriesChanged(uid, TEST_CONTROL_CATEGORIES);
+
+ mManager.removeCallback(mockCallback);
+ }
+
+}
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynandroid/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynandroid/DynamicSystemInstallationService.java
index df2c571..e479e38 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynandroid/DynamicSystemInstallationService.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynandroid/DynamicSystemInstallationService.java
@@ -49,6 +49,7 @@
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
+import android.os.ParcelableException;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.image.DynamicSystemClient;
@@ -170,13 +171,13 @@
@Override
public void onProgressUpdate(long installedSize) {
mInstalledSize = installedSize;
- postStatus(STATUS_IN_PROGRESS, CAUSE_NOT_SPECIFIED);
+ postStatus(STATUS_IN_PROGRESS, CAUSE_NOT_SPECIFIED, null);
}
@Override
- public void onResult(int result) {
+ public void onResult(int result, Throwable detail) {
if (result == RESULT_OK) {
- postStatus(STATUS_READY, CAUSE_INSTALL_COMPLETED);
+ postStatus(STATUS_READY, CAUSE_INSTALL_COMPLETED, null);
return;
}
@@ -185,15 +186,15 @@
switch (result) {
case RESULT_ERROR_IO:
- postStatus(STATUS_NOT_STARTED, CAUSE_ERROR_IO);
+ postStatus(STATUS_NOT_STARTED, CAUSE_ERROR_IO, detail);
break;
case RESULT_ERROR_INVALID_URL:
- postStatus(STATUS_NOT_STARTED, CAUSE_ERROR_INVALID_URL);
+ postStatus(STATUS_NOT_STARTED, CAUSE_ERROR_INVALID_URL, detail);
break;
case RESULT_ERROR_EXCEPTION:
- postStatus(STATUS_NOT_STARTED, CAUSE_ERROR_EXCEPTION);
+ postStatus(STATUS_NOT_STARTED, CAUSE_ERROR_EXCEPTION, detail);
break;
}
}
@@ -201,7 +202,7 @@
@Override
public void onCancelled() {
resetTaskAndStop();
- postStatus(STATUS_NOT_STARTED, CAUSE_INSTALL_CANCELLED);
+ postStatus(STATUS_NOT_STARTED, CAUSE_INSTALL_CANCELLED, null);
}
private void executeInstallCommand(Intent intent) {
@@ -266,7 +267,7 @@
Toast.LENGTH_LONG).show();
resetTaskAndStop();
- postStatus(STATUS_NOT_STARTED, CAUSE_INSTALL_CANCELLED);
+ postStatus(STATUS_NOT_STARTED, CAUSE_INSTALL_CANCELLED, null);
mDynSystem.remove();
}
@@ -414,7 +415,7 @@
return VerificationActivity.isVerified(url);
}
- private void postStatus(int status, int cause) {
+ private void postStatus(int status, int cause, Throwable detail) {
Log.d(TAG, "postStatus(): statusCode=" + status + ", causeCode=" + cause);
boolean notifyOnNotificationBar = true;
@@ -433,18 +434,24 @@
for (int i = mClients.size() - 1; i >= 0; i--) {
try {
- notifyOneClient(mClients.get(i), status, cause);
+ notifyOneClient(mClients.get(i), status, cause, detail);
} catch (RemoteException e) {
mClients.remove(i);
}
}
}
- private void notifyOneClient(Messenger client, int status, int cause) throws RemoteException {
+ private void notifyOneClient(Messenger client, int status, int cause, Throwable detail)
+ throws RemoteException {
Bundle bundle = new Bundle();
bundle.putLong(DynamicSystemClient.KEY_INSTALLED_SIZE, mInstalledSize);
+ if (detail != null) {
+ bundle.putSerializable(DynamicSystemClient.KEY_EXCEPTION_DETAIL,
+ new ParcelableException(detail));
+ }
+
client.send(Message.obtain(null,
DynamicSystemClient.MSG_POST_STATUS, status, cause, bundle));
}
@@ -496,7 +503,7 @@
int status = getStatus();
// tell just registered client my status, but do not specify cause
- notifyOneClient(client, status, CAUSE_NOT_SPECIFIED);
+ notifyOneClient(client, status, CAUSE_NOT_SPECIFIED, null);
mClients.add(client);
} catch (RemoteException e) {
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynandroid/InstallationAsyncTask.java b/packages/DynamicSystemInstallationService/src/com/android/dynandroid/InstallationAsyncTask.java
index 052fc0a..aee5de5 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynandroid/InstallationAsyncTask.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynandroid/InstallationAsyncTask.java
@@ -31,7 +31,7 @@
import java.util.zip.GZIPInputStream;
-class InstallationAsyncTask extends AsyncTask<String, Long, Integer> {
+class InstallationAsyncTask extends AsyncTask<String, Long, Throwable> {
private static final String TAG = "InstallationAsyncTask";
@@ -43,7 +43,6 @@
}
}
-
/** Not completed, including being cancelled */
static final int NO_RESULT = 0;
static final int RESULT_OK = 1;
@@ -53,7 +52,7 @@
interface InstallStatusListener {
void onProgressUpdate(long installedSize);
- void onResult(int resultCode);
+ void onResult(int resultCode, Throwable detail);
void onCancelled();
}
@@ -84,7 +83,7 @@
}
@Override
- protected Integer doInBackground(String... voids) {
+ protected Throwable doInBackground(String... voids) {
Log.d(TAG, "Start doInBackground(), URL: " + mUrl);
try {
@@ -108,7 +107,7 @@
if (isCancelled()) {
boolean aborted = mDynSystem.abort();
Log.d(TAG, "Called DynamicSystemManager.abort(), result = " + aborted);
- return RESULT_OK;
+ return null;
}
GsiProgress progress = mDynSystem.getInstallationProgress();
@@ -124,10 +123,8 @@
if (mInstallationSession == null) {
- Log.e(TAG, "Failed to start installation with requested size: "
+ throw new IOException("Failed to start installation with requested size: "
+ (mSystemSize + mUserdataSize));
-
- return RESULT_ERROR_IO;
}
installedSize = mUserdataSize;
@@ -157,20 +154,11 @@
}
}
- return RESULT_OK;
-
- } catch (IOException e) {
- e.printStackTrace();
- return RESULT_ERROR_IO;
-
- } catch (InvalidImageUrlException e) {
- e.printStackTrace();
- return RESULT_ERROR_INVALID_URL;
+ return null;
} catch (Exception e) {
e.printStackTrace();
- return RESULT_ERROR_EXCEPTION;
-
+ return e;
} finally {
close();
}
@@ -180,19 +168,24 @@
protected void onCancelled() {
Log.d(TAG, "onCancelled(), URL: " + mUrl);
- close();
-
mListener.onCancelled();
}
@Override
- protected void onPostExecute(Integer result) {
- Log.d(TAG, "onPostExecute(), URL: " + mUrl + ", result: " + result);
+ protected void onPostExecute(Throwable detail) {
+ if (detail == null) {
+ mResult = RESULT_OK;
+ } else if (detail instanceof IOException) {
+ mResult = RESULT_ERROR_IO;
+ } else if (detail instanceof InvalidImageUrlException) {
+ mResult = RESULT_ERROR_INVALID_URL;
+ } else {
+ mResult = RESULT_ERROR_EXCEPTION;
+ }
- close();
+ Log.d(TAG, "onPostExecute(), URL: " + mUrl + ", result: " + mResult);
- mResult = result;
- mListener.onResult(mResult);
+ mListener.onResult(mResult, detail);
}
@Override
diff --git a/packages/SystemUI/res/layout-land/volume_dialog.xml b/packages/SystemUI/res/layout-land/volume_dialog.xml
index c1e74ef..9acfbaf 100644
--- a/packages/SystemUI/res/layout-land/volume_dialog.xml
+++ b/packages/SystemUI/res/layout-land/volume_dialog.xml
@@ -15,6 +15,7 @@
-->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:sysui="http://schemas.android.com/apk/res-auto"
android:id="@+id/volume_dialog_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -116,16 +117,17 @@
android:clipToPadding="false"
android:translationZ="@dimen/volume_dialog_elevation"
android:background="@drawable/rounded_bg_full">
- <com.android.keyguard.AlphaOptimizedImageButton
+ <com.android.systemui.volume.CaptionsToggleImageButton
android:id="@+id/odi_captions_icon"
android:src="@drawable/ic_volume_odi_captions_disabled"
style="@style/VolumeButtons"
android:background="@drawable/rounded_ripple"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:tint="@color/accent_tint_color_selector"
+ android:tint="@color/caption_tint_color_selector"
android:layout_gravity="center"
- android:soundEffectsEnabled="false" />
+ android:soundEffectsEnabled="false"
+ sysui:optedOut="false"/>
</FrameLayout>
<ViewStub
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index aca99e3..3aba6a0 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2234,6 +2234,9 @@
<!-- Quick settings tile secondary label format combining roaming with the mobile data type. [CHAR LIMIT=NONE] -->
<string name="mobile_data_text_format"><xliff:g name="roaming_status" example="Roaming">%1$s</xliff:g> \u2014 <xliff:g name="mobile_data_type" example="LTE">%2$s</xliff:g></string>
+ <!-- Quick settings tile secondary label format combining carrier name with the mobile data tye. [CHAR LIMIT=NONE] -->
+ <string name="mobile_carrier_text_format"><xliff:g id="carrier_name" example="Test">%1$s</xliff:g>, <xliff:g id="mobile_data_type" example="LTE">%2$s</xliff:g></string>
+
<!-- Label for when wifi is off in QS detail panel [CHAR LIMIT=NONE] -->
<string name="wifi_is_off">Wi-Fi is off</string>
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
index 294c725..8ad5c7b 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
@@ -39,6 +39,7 @@
import androidx.annotation.VisibleForTesting;
import com.android.internal.colorextraction.ColorExtractor;
+import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dock.DockManager.DockEventListener;
@@ -72,6 +73,7 @@
private final Map<String, ClockPlugin> mClocks = new ArrayMap<>();
@Nullable private ClockPlugin mCurrentClock;
+ private final Context mContext;
private final ContentResolver mContentResolver;
private final SettingsWrapper mSettingsWrapper;
private final Handler mMainHandler = new Handler(Looper.getMainLooper());
@@ -117,7 +119,7 @@
reload();
}
};
- @Nullable private final DockManager mDockManager;
+ @Nullable private DockManager mDockManager;
/**
* When docked, the DOCKED_CLOCK_FACE setting will be checked for the custom clock face
* to show.
@@ -132,18 +134,16 @@
@Inject
public ClockManager(Context context, InjectionInflationController injectionInflater,
- PluginManager pluginManager, @Nullable DockManager dockManager,
- SysuiColorExtractor colorExtractor) {
- this(context, injectionInflater, pluginManager, dockManager, colorExtractor,
+ PluginManager pluginManager, SysuiColorExtractor colorExtractor) {
+ this(context, injectionInflater, pluginManager, colorExtractor,
context.getContentResolver(), new SettingsWrapper(context.getContentResolver()));
}
ClockManager(Context context, InjectionInflationController injectionInflater,
- PluginManager pluginManager, @Nullable DockManager dockManager,
- SysuiColorExtractor colorExtractor, ContentResolver contentResolver,
- SettingsWrapper settingsWrapper) {
+ PluginManager pluginManager, SysuiColorExtractor colorExtractor,
+ ContentResolver contentResolver, SettingsWrapper settingsWrapper) {
+ mContext = context;
mPluginManager = pluginManager;
- mDockManager = dockManager;
mColorExtractor = colorExtractor;
mContentResolver = contentResolver;
mSettingsWrapper = settingsWrapper;
@@ -323,6 +323,9 @@
mContentResolver.registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.DOCKED_CLOCK_FACE),
false, mContentObserver);
+ if (mDockManager == null) {
+ mDockManager = SysUiServiceProvider.getComponent(mContext, DockManager.class);
+ }
if (mDockManager != null) {
mDockManager.addListener(mDockEventListener);
}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 6bb4fb5..47ad0c1 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -31,7 +31,6 @@
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.classifier.FalsingManager;
-import com.android.systemui.dock.DockManager;
import com.android.systemui.fragments.FragmentService;
import com.android.systemui.keyguard.DismissCallbackRegistry;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -220,16 +219,6 @@
return SysUiServiceProvider.getComponent(context, StatusBar.class);
}
- /**
- * Provides DockManager.
- */
- @Singleton
- @Provides
- @Nullable
- public DockManager providesDockManager(Context context) {
- return SysUiServiceProvider.getComponent(context, DockManager.class);
- }
-
@Module
protected static class ContextHolder {
private Context mContext;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
index 7a68be4..979d226 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
@@ -84,7 +84,6 @@
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- updateViews();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index c587a39..db79e4d7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -25,11 +25,7 @@
import android.content.res.Resources;
import android.provider.Settings;
import android.service.quicksettings.Tile;
-import android.text.SpannableString;
-import android.text.SpannableStringBuilder;
-import android.text.Spanned;
import android.text.TextUtils;
-import android.text.style.TextAppearanceSpan;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -192,8 +188,10 @@
state.secondaryLabel = r.getString(R.string.status_bar_airplane);
} else if (mobileDataEnabled) {
state.state = Tile.STATE_ACTIVE;
- state.secondaryLabel = appendMobileDataType(getMobileDataSubscriptionName(cb),
- cb.dataContentDescription);
+ state.secondaryLabel = appendMobileDataType(
+ // Only show carrier name if there are more than 1 subscription
+ cb.multipleSubs ? cb.dataSubscriptionName : "",
+ getMobileDataContentName(cb));
} else {
state.state = Tile.STATE_INACTIVE;
state.secondaryLabel = r.getString(R.string.cell_data_off);
@@ -216,24 +214,22 @@
if (TextUtils.isEmpty(dataType)) {
return current;
}
- SpannableString type = new SpannableString(dataType);
- SpannableStringBuilder builder = new SpannableStringBuilder(current);
- builder.append(" ");
- builder.append(type, new TextAppearanceSpan(mContext, R.style.TextAppearance_RATBadge),
- Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- return builder;
+ if (TextUtils.isEmpty(current)) {
+ return dataType;
+ }
+ return mContext.getString(R.string.mobile_carrier_text_format, current, dataType);
}
- private CharSequence getMobileDataSubscriptionName(CallbackInfo cb) {
- if (cb.roaming && !TextUtils.isEmpty(cb.dataSubscriptionName)) {
+ private CharSequence getMobileDataContentName(CallbackInfo cb) {
+ if (cb.roaming && !TextUtils.isEmpty(cb.dataContentDescription)) {
String roaming = mContext.getString(R.string.data_connection_roaming);
- String dataDescription = cb.dataSubscriptionName.toString();
+ String dataDescription = cb.dataContentDescription.toString();
return mContext.getString(R.string.mobile_data_text_format, roaming, dataDescription);
}
if (cb.roaming) {
return mContext.getString(R.string.data_connection_roaming);
}
- return cb.dataSubscriptionName;
+ return cb.dataContentDescription;
}
@Override
@@ -254,6 +250,7 @@
boolean activityOut;
boolean noSim;
boolean roaming;
+ boolean multipleSubs;
}
private final class CellSignalCallback implements SignalCallback {
@@ -272,6 +269,7 @@
mInfo.activityIn = activityIn;
mInfo.activityOut = activityOut;
mInfo.roaming = roaming;
+ mInfo.multipleSubs = mController.getNumberSubscriptions() > 1;
refreshState(mInfo);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java
index 4c7fdb0..4897464 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java
@@ -108,7 +108,7 @@
public static NavigationBarEdgePanel create(@NonNull Context context, int width, int height,
int gravity) {
final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(width, height,
- WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
+ WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index f809b62..bfbe886 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -346,8 +346,11 @@
post(() -> {
// When the ime changes visibility, resize the edge panels to not cover the ime
final int width = mPrototypeController.getEdgeSensitivityWidth();
- final int height = mContext.getDisplay().getHeight() - imeHeight
- - getResources().getDimensionPixelOffset(R.dimen.status_bar_height);
+ int height = mContext.getDisplay().getHeight() - imeHeight;
+ if (!imeVisible) {
+ // Hide the navigation bar area at the bottom for gestures
+ height -= getResources().getDimensionPixelOffset(R.dimen.navigation_bar_height);
+ }
if (mLeftEdgePanel != null) {
mLeftEdgePanel.setDimensions(width, height);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index 5bd394f..c5996a1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -470,10 +470,16 @@
mNetworkController.recalculateEmergency();
}
// Fill in the network name if we think we have it.
- if (mCurrentState.networkName == mNetworkNameDefault && mServiceState != null
+ if (mCurrentState.networkName.equals(mNetworkNameDefault) && mServiceState != null
&& !TextUtils.isEmpty(mServiceState.getOperatorAlphaShort())) {
mCurrentState.networkName = mServiceState.getOperatorAlphaShort();
}
+ // If this is the data subscription, update the currentState data name
+ if (mCurrentState.networkNameData.equals(mNetworkNameDefault) && mServiceState != null
+ && mCurrentState.dataSim
+ && !TextUtils.isEmpty(mServiceState.getDataOperatorAlphaShort())) {
+ mCurrentState.networkNameData = mServiceState.getDataOperatorAlphaShort();
+ }
notifyListenersIfNecessary();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index 51fef7d..71db618 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -37,6 +37,7 @@
DataUsageController getMobileDataController();
DataSaverController getDataSaverController();
String getMobileDataNetworkName();
+ int getNumberSubscriptions();
boolean hasVoiceCallingFeature();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index ef39912..d01430a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -376,6 +376,11 @@
return controller != null ? controller.getState().networkNameData : "";
}
+ @Override
+ public int getNumberSubscriptions() {
+ return mMobileSignalControllers.size();
+ }
+
public boolean isEmergencyOnly() {
if (mMobileSignalControllers.size() == 0) {
// When there are no active subscriptions, determine emengency state from last
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java
index 36265d4..46b1833 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java
@@ -67,8 +67,10 @@
when(mMockInjectionInflationController.injectable(any())).thenReturn(inflater);
mFakeDockManager = new DockManagerFake();
+ getContext().putComponent(DockManager.class, mFakeDockManager);
+
mClockManager = new ClockManager(getContext(), mMockInjectionInflationController,
- mMockPluginManager, mFakeDockManager, mMockColorExtractor, mMockContentResolver,
+ mMockPluginManager, mMockColorExtractor, mMockContentResolver,
mMockSettingsWrapper);
mClockManager.addOnClockChangedListener(mMockListener);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index 5cafc02..4fe18b4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -448,7 +448,12 @@
}
}
- protected void assertNetworkNameEquals(String expected) {
+ protected void assertNetworkNameEquals(String expected) {
assertEquals("Network name", expected, mMobileSignalController.getState().networkName);
- }
+ }
+
+ protected void assertDataNetworkNameEquals(String expected) {
+ assertEquals("Data network name", expected, mNetworkController.getMobileDataNetworkName());
+ }
+
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
index 68323c9..cd0a0441 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
@@ -285,6 +285,15 @@
testDataActivity(TelephonyManager.DATA_ACTIVITY_INOUT, true, true);
}
+ @Test
+ public void testUpdateDataNetworkName() {
+ setupDefaultSignal();
+ String newDataName = "TestDataName";
+ when(mServiceState.getDataOperatorAlphaShort()).thenReturn(newDataName);
+ updateServiceState();
+ assertDataNetworkNameEquals(newDataName);
+ }
+
private void testDataActivity(int direction, boolean in, boolean out) {
updateDataActivity(direction);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
index bcbba8b..ac6544e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
@@ -20,6 +20,7 @@
import static junit.framework.Assert.assertTrue;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
import android.content.Intent;
import android.net.ConnectivityManager;
@@ -55,7 +56,7 @@
@Test
public void testNoIconWithoutMobile() {
// Turn off mobile network support.
- Mockito.when(mMockCm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)).thenReturn(false);
+ when(mMockCm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)).thenReturn(false);
// Create a new NetworkController as this is currently handled in constructor.
mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm, mMockWm, mMockSm,
mConfig, Looper.getMainLooper(), mCallbackHandler,
@@ -117,7 +118,7 @@
@Test
public void testNoSimlessIconWithoutMobile() {
// Turn off mobile network support.
- Mockito.when(mMockCm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)).thenReturn(false);
+ when(mMockCm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)).thenReturn(false);
// Create a new NetworkController as this is currently handled in constructor.
mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm, mMockWm, mMockSm,
mConfig, Looper.getMainLooper(), mCallbackHandler,
@@ -253,14 +254,14 @@
// Generate a list of subscriptions we will tell the NetworkController to use.
SubscriptionInfo mockSubInfo = Mockito.mock(SubscriptionInfo.class);
- Mockito.when(mockSubInfo.getSubscriptionId()).thenReturn(testSubscriptions[i]);
+ when(mockSubInfo.getSubscriptionId()).thenReturn(testSubscriptions[i]);
subscriptions.add(mockSubInfo);
}
assertTrue(mNetworkController.hasCorrectMobileControllers(subscriptions));
// Add a subscription that the NetworkController doesn't know about.
SubscriptionInfo mockSubInfo = Mockito.mock(SubscriptionInfo.class);
- Mockito.when(mockSubInfo.getSubscriptionId()).thenReturn(notTestSubscription);
+ when(mockSubInfo.getSubscriptionId()).thenReturn(notTestSubscription);
subscriptions.add(mockSubInfo);
assertFalse(mNetworkController.hasCorrectMobileControllers(subscriptions));
}
@@ -290,8 +291,8 @@
if (i != indexToSkipSubscription) {
// Generate a list of subscriptions we will tell the NetworkController to use.
SubscriptionInfo mockSubInfo = Mockito.mock(SubscriptionInfo.class);
- Mockito.when(mockSubInfo.getSubscriptionId()).thenReturn(testSubscriptions[i]);
- Mockito.when(mockSubInfo.getSimSlotIndex()).thenReturn(testSubscriptions[i]);
+ when(mockSubInfo.getSubscriptionId()).thenReturn(testSubscriptions[i]);
+ when(mockSubInfo.getSimSlotIndex()).thenReturn(testSubscriptions[i]);
subscriptions.add(mockSubInfo);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java
index 5385f6d..d5ba381 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java
@@ -93,4 +93,9 @@
public String getMobileDataNetworkName() {
return "";
}
+
+ @Override
+ public int getNumberSubscriptions() {
+ return 0;
+ }
}
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index 95abd05..f629cf6 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -7149,6 +7149,13 @@
// Settings > Display > Theme
DARK_UI_SETTINGS = 1698;
+
+ // Settings > global bubble settings
+ BUBBLE_SETTINGS = 1699;
+
+ // Settings > app > bubble settings
+ APP_BUBBLE_SETTINGS = 1700;
+
// ---- End Q Constants, all Q constants go above this line ----
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 47cd917..904817e 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -777,6 +777,7 @@
final int removedWindowId = removeAccessibilityInteractionConnectionInternalLocked(
token, mGlobalWindowTokens, mGlobalInteractionConnections);
if (removedWindowId >= 0) {
+ mSecurityPolicy.onAccessibilityClientRemovedLocked(removedWindowId);
if (DEBUG) {
Slog.i(LOG_TAG, "Removed global connection for pid:" + Binder.getCallingPid()
+ " with windowId: " + removedWindowId + " and token: " + window.asBinder());
@@ -790,6 +791,7 @@
removeAccessibilityInteractionConnectionInternalLocked(
token, userState.mWindowTokens, userState.mInteractionConnections);
if (removedWindowIdForUser >= 0) {
+ mSecurityPolicy.onAccessibilityClientRemovedLocked(removedWindowIdForUser);
if (DEBUG) {
Slog.i(LOG_TAG, "Removed user connection for pid:" + Binder.getCallingPid()
+ " with windowId: " + removedWindowIdForUser + " and userId:"
@@ -1332,6 +1334,7 @@
userState.mWindowTokens.remove(windowId);
userState.mInteractionConnections.remove(windowId);
}
+ mSecurityPolicy.onAccessibilityClientRemovedLocked(windowId);
if (DEBUG) {
Slog.i(LOG_TAG, "Removing interaction connection to windowId: " + windowId);
}
@@ -3266,6 +3269,18 @@
mWindows = null;
}
+ /**
+ * A callback when accessibility interaction client is removed.
+ */
+ public void onAccessibilityClientRemovedLocked(int windowId) {
+ // Active window cannot update immediately, if windows callback is unregistered.
+ // Update active window to invalid, when its a11y interaction client is removed.
+ if (mWindowsForAccessibilityCallback == null && windowId >= 0
+ && mActiveWindowId == windowId) {
+ mActiveWindowId = INVALID_WINDOW_ID;
+ }
+ }
+
public void updateWindowsLocked(List<WindowInfo> windows) {
if (mWindows == null) {
mWindows = new ArrayList<>();
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 98311fc..5b9c1f8 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -2881,6 +2881,32 @@
mVold.commitChanges();
}
+ /**
+ * Check if we should be mounting with checkpointing or are checkpointing now
+ */
+ @Override
+ public boolean needsCheckpoint() throws RemoteException {
+ // Only the system process is permitted to commit checkpoints
+ if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) {
+ throw new SecurityException("no permission to commit checkpoint changes");
+ }
+
+ return mVold.needsCheckpoint();
+ }
+
+ /**
+ * Abort the current set of changes and either try again, or abort entirely
+ */
+ @Override
+ public void abortChanges(String message, boolean retry) throws RemoteException {
+ // Only the system process is permitted to abort checkpoints
+ if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) {
+ throw new SecurityException("no permission to commit checkpoint changes");
+ }
+
+ mVold.abortChanges(message, retry);
+ }
+
@Override
public String getPassword() throws RemoteException {
mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
@@ -3871,6 +3897,7 @@
case "com.jrtstudio.AnotherMusicPlayer": // b/129084562
case "ak.alizandro.smartaudiobookplayer": // b/129084042
case "com.campmobile.snow": // b/128803870
+ case "com.qnap.qfile": // b/126374406
return Zygote.MOUNT_EXTERNAL_LEGACY;
}
}
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 26896f5..8b10267 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -73,10 +73,12 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
+import java.util.OptionalInt;
/**
* Since phone process can be restarted, this class provides a centralized place
@@ -1159,17 +1161,28 @@
@Override
public void notifyCarrierNetworkChange(boolean active) {
- enforceNotifyPermissionOrCarrierPrivilege("notifyCarrierNetworkChange()");
+ // only CarrierService with carrier privilege rule should have the permission.
+ int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ try {
+ subId = Arrays.stream(SubscriptionManager.from(mContext)
+ .getActiveSubscriptionIdList())
+ .filter(i -> TelephonyPermissions.checkCarrierPrivilegeForSubId(i))
+ .findFirst().getAsInt();
+ } catch (NoSuchElementException ex) {
+ log("notifyCarrierNetworkChange without carrier privilege");
+ }
+ int phoneId = SubscriptionManager.getPhoneId(subId);
if (VDBG) {
- log("notifyCarrierNetworkChange: active=" + active);
+ log("notifyCarrierNetworkChange: active=" + active + "subId: " + subId);
}
synchronized (mRecords) {
mCarrierNetworkChangeState = active;
for (Record r : mRecords) {
if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.LISTEN_CARRIER_NETWORK_CHANGE)) {
+ PhoneStateListener.LISTEN_CARRIER_NETWORK_CHANGE) &&
+ idMatch(r.subId, subId, phoneId)) {
try {
r.callback.onCarrierNetworkChange(active);
} catch (RemoteException ex) {
@@ -1885,7 +1898,7 @@
pw.println("mDataConnectionState=" + mDataConnectionState[i]);
pw.println("mCellLocation=" + mCellLocation[i]);
pw.println("mCellInfo=" + mCellInfo.get(i));
- pw.println("mImsCallDisconnectCause=" + mImsReasonInfo.get(i).toString());
+ pw.println("mImsCallDisconnectCause=" + mImsReasonInfo.get(i));
pw.decreaseIndent();
}
pw.println("mCallNetworkType=" + mCallNetworkType);
diff --git a/services/core/java/com/android/server/accounts/AccountsDb.java b/services/core/java/com/android/server/accounts/AccountsDb.java
index 712edcc..da66590 100644
--- a/services/core/java/com/android/server/accounts/AccountsDb.java
+++ b/services/core/java/com/android/server/accounts/AccountsDb.java
@@ -58,7 +58,6 @@
private static final int CE_DATABASE_VERSION = 10;
private static final int DE_DATABASE_VERSION = 3; // Added visibility support in O
-
static final String TABLE_ACCOUNTS = "accounts";
private static final String ACCOUNTS_ID = "_id";
private static final String ACCOUNTS_NAME = "name";
@@ -267,6 +266,13 @@
}
@Override
+ public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ Log.e(TAG, "onDowngrade: recreate accounts CE table");
+ resetDatabase(db);
+ onCreate(db);
+ }
+
+ @Override
public void onOpen(SQLiteDatabase db) {
if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + CE_DATABASE_NAME);
}
@@ -616,6 +622,13 @@
}
}
+ @Override
+ public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ Log.e(TAG, "onDowngrade: recreate accounts DE table");
+ resetDatabase(db);
+ onCreate(db);
+ }
+
public SQLiteDatabase getReadableDatabaseUserIsUnlocked() {
if(!mCeAttached) {
Log.wtf(TAG, "getReadableDatabaseUserIsUnlocked called while user " + mUserId
@@ -1399,4 +1412,26 @@
return new AccountsDb(deDatabaseHelper, context, preNDatabaseFile);
}
+ /**
+ * Removes all tables and triggers created by AccountManager.
+ */
+ private static void resetDatabase(SQLiteDatabase db) {
+ try (Cursor c = db.rawQuery("SELECT name FROM sqlite_master WHERE type ='table'", null)) {
+ while (c.moveToNext()) {
+ String name = c.getString(0);
+ // Skip tables managed by SQLiteDatabase
+ if ("android_metadata".equals(name) || "sqlite_sequence".equals(name)) {
+ continue;
+ }
+ db.execSQL("DROP TABLE IF EXISTS " + name);
+ }
+ }
+
+ try (Cursor c = db.rawQuery("SELECT name FROM sqlite_master WHERE type ='trigger'", null)) {
+ while (c.moveToNext()) {
+ String name = c.getString(0);
+ db.execSQL("DROP TRIGGER IF EXISTS " + name);
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
new file mode 100644
index 0000000..d284c60
--- /dev/null
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
@@ -0,0 +1,341 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.media;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.media.IMediaRoute2Callback;
+import android.media.IMediaRoute2Provider;
+import android.media.MediaRoute2ProviderService;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.IBinder.DeathRecipient;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Log;
+import android.util.Slog;
+
+import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
+
+/**
+ * Maintains a connection to a particular media route provider service.
+ */
+final class MediaRoute2ProviderProxy implements ServiceConnection {
+ private static final String TAG = "MediaRoute2ProviderProxy";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private final Context mContext;
+ private final ComponentName mComponentName;
+ private final int mUserId;
+ private final Handler mHandler;
+
+ private Callback mCallback;
+
+ // Selected Route info
+ public int mSelectedUid;
+ public String mSelectedRouteId;
+
+ // Connection state
+ private boolean mRunning;
+ private boolean mBound;
+ private Connection mActiveConnection;
+ private boolean mConnectionReady;
+
+ MediaRoute2ProviderProxy(Context context, ComponentName componentName, int userId) {
+ mContext = context;
+ mComponentName = componentName;
+ mUserId = userId;
+ mHandler = new Handler();
+ }
+
+ public void dump(PrintWriter pw, String prefix) {
+ pw.println(prefix + "Proxy");
+ pw.println(prefix + " mUserId=" + mUserId);
+ pw.println(prefix + " mRunning=" + mRunning);
+ pw.println(prefix + " mBound=" + mBound);
+ pw.println(prefix + " mActiveConnection=" + mActiveConnection);
+ pw.println(prefix + " mConnectionReady=" + mConnectionReady);
+ }
+
+ public void setCallback(Callback callback) {
+ mCallback = callback;
+ }
+
+ public void setSelectedRoute(int uid, String routeId) {
+ if (mConnectionReady) {
+ mActiveConnection.selectRoute(uid, routeId);
+ updateBinding();
+ }
+ }
+
+ public boolean hasComponentName(String packageName, String className) {
+ return mComponentName.getPackageName().equals(packageName)
+ && mComponentName.getClassName().equals(className);
+ }
+
+ public String getFlattenedComponentName() {
+ return mComponentName.flattenToShortString();
+ }
+
+ public void start() {
+ if (!mRunning) {
+ if (DEBUG) {
+ Slog.d(TAG, this + ": Starting");
+ }
+
+ mRunning = true;
+ updateBinding();
+ }
+ }
+
+ public void stop() {
+ if (mRunning) {
+ if (DEBUG) {
+ Slog.d(TAG, this + ": Stopping");
+ }
+
+ mRunning = false;
+ updateBinding();
+ }
+ }
+
+ public void rebindIfDisconnected() {
+ if (mActiveConnection == null && shouldBind()) {
+ unbind();
+ bind();
+ }
+ }
+
+ private void updateBinding() {
+ if (shouldBind()) {
+ bind();
+ } else {
+ unbind();
+ }
+ }
+
+ private boolean shouldBind() {
+ //TODO: binding could be delayed until it's necessary.
+ if (mRunning) {
+ return true;
+ }
+ return false;
+ }
+
+ private void bind() {
+ if (!mBound) {
+ if (DEBUG) {
+ Slog.d(TAG, this + ": Binding");
+ }
+
+ Intent service = new Intent(MediaRoute2ProviderService.SERVICE_INTERFACE);
+ service.setComponent(mComponentName);
+ try {
+ mBound = mContext.bindServiceAsUser(service, this,
+ Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
+ new UserHandle(mUserId));
+ if (!mBound && DEBUG) {
+ Slog.d(TAG, this + ": Bind failed");
+ }
+ } catch (SecurityException ex) {
+ if (DEBUG) {
+ Slog.d(TAG, this + ": Bind failed", ex);
+ }
+ }
+ }
+ }
+
+ private void unbind() {
+ if (mBound) {
+ if (DEBUG) {
+ Slog.d(TAG, this + ": Unbinding");
+ }
+
+ mBound = false;
+ disconnect();
+ mContext.unbindService(this);
+ }
+ }
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ if (DEBUG) {
+ Slog.d(TAG, this + ": Connected");
+ }
+
+ if (mBound) {
+ disconnect();
+
+ IMediaRoute2Provider provider = IMediaRoute2Provider.Stub.asInterface(service);
+ if (provider != null) {
+ Connection connection = new Connection(provider);
+ if (connection.register()) {
+ mActiveConnection = connection;
+ } else {
+ if (DEBUG) {
+ Slog.d(TAG, this + ": Registration failed");
+ }
+ }
+ } else {
+ Slog.e(TAG, this + ": Service returned invalid remote display provider binder");
+ }
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ if (DEBUG) {
+ Slog.d(TAG, this + ": Service disconnected");
+ }
+ disconnect();
+ }
+
+ private void onConnectionReady(Connection connection) {
+ if (mActiveConnection == connection) {
+ mConnectionReady = true;
+ }
+ }
+
+ private void onConnectionDied(Connection connection) {
+ if (mActiveConnection == connection) {
+ if (DEBUG) {
+ Slog.d(TAG, this + ": Service connection died");
+ }
+ disconnect();
+ }
+ }
+
+ private void onRouteSelected(Connection connection, int uid, String routeId) {
+ mSelectedUid = uid;
+ mSelectedRouteId = routeId;
+
+ if (mActiveConnection == connection) {
+ if (DEBUG) {
+ Slog.d(TAG, this + ": State changed ");
+ }
+ mHandler.post(mStateChanged);
+ }
+ }
+
+ private void disconnect() {
+ if (mActiveConnection != null) {
+ mConnectionReady = false;
+ mActiveConnection.dispose();
+ mActiveConnection = null;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "Service connection " + mComponentName.flattenToShortString();
+ }
+
+ private final Runnable mStateChanged = new Runnable() {
+ @Override
+ public void run() {
+ if (mCallback != null) {
+ mCallback.onProviderStateChanged(MediaRoute2ProviderProxy.this);
+ }
+ }
+ };
+
+ public interface Callback {
+ void onProviderStateChanged(MediaRoute2ProviderProxy provider);
+ }
+
+ private final class Connection implements DeathRecipient {
+ private final IMediaRoute2Provider mProvider;
+ private final ProviderCallback mCallback;
+
+ Connection(IMediaRoute2Provider provider) {
+ mProvider = provider;
+ mCallback = new ProviderCallback(this);
+ }
+
+ public boolean register() {
+ try {
+ mProvider.asBinder().linkToDeath(this, 0);
+ mProvider.setCallback(mCallback);
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ onConnectionReady(Connection.this);
+ }
+ });
+ return true;
+ } catch (RemoteException ex) {
+ binderDied();
+ }
+ return false;
+ }
+
+ public void dispose() {
+ mProvider.asBinder().unlinkToDeath(this, 0);
+ mCallback.dispose();
+ }
+
+ public void selectRoute(int uid, String id) {
+ try {
+ mProvider.selectRoute(uid, id);
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "Failed to deliver request to set discovery mode.", ex);
+ }
+ }
+
+ @Override
+ public void binderDied() {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ onConnectionDied(Connection.this);
+ }
+ });
+ }
+
+ void postRouteSelected(int uid, String routeId) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ onRouteSelected(Connection.this, uid, routeId);
+ }
+ });
+ }
+ }
+
+ private static final class ProviderCallback extends IMediaRoute2Callback.Stub {
+ private final WeakReference<Connection> mConnectionRef;
+
+ ProviderCallback(Connection connection) {
+ mConnectionRef = new WeakReference<Connection>(connection);
+ }
+
+ public void dispose() {
+ mConnectionRef.clear();
+ }
+
+ @Override
+ public void onRouteSelected(int uid, String routeId) throws RemoteException {
+ Connection connection = mConnectionRef.get();
+ if (connection != null) {
+ connection.postRouteSelected(uid, routeId);
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderWatcher.java b/services/core/java/com/android/server/media/MediaRoute2ProviderWatcher.java
new file mode 100644
index 0000000..08d8c58
--- /dev/null
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderWatcher.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.media;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.media.MediaRoute2ProviderService;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.util.Log;
+import android.util.Slog;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+
+/**
+ */
+final class MediaRoute2ProviderWatcher {
+ private static final String TAG = "MediaRouteProvider"; // max. 23 chars
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private final Context mContext;
+ private final Callback mCallback;
+ private final Handler mHandler;
+ private final int mUserId;
+ private final PackageManager mPackageManager;
+
+ private final ArrayList<MediaRoute2ProviderProxy> mProviders = new ArrayList<>();
+ private boolean mRunning;
+
+ MediaRoute2ProviderWatcher(Context context,
+ Callback callback, Handler handler, int userId) {
+ mContext = context;
+ mCallback = callback;
+ mHandler = handler;
+ mUserId = userId;
+ mPackageManager = context.getPackageManager();
+ }
+
+ public void dump(PrintWriter pw, String prefix) {
+ pw.println(prefix + "Watcher");
+ pw.println(prefix + " mUserId=" + mUserId);
+ pw.println(prefix + " mRunning=" + mRunning);
+ pw.println(prefix + " mProviders.size()=" + mProviders.size());
+ }
+
+ public void start() {
+ if (!mRunning) {
+ mRunning = true;
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
+ filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
+ filter.addDataScheme("package");
+ mContext.registerReceiverAsUser(mScanPackagesReceiver,
+ new UserHandle(mUserId), filter, null, mHandler);
+
+ // Scan packages.
+ // Also has the side-effect of restarting providers if needed.
+ mHandler.post(mScanPackagesRunnable);
+ }
+ }
+
+ public void stop() {
+ if (mRunning) {
+ mRunning = false;
+
+ mContext.unregisterReceiver(mScanPackagesReceiver);
+ mHandler.removeCallbacks(mScanPackagesRunnable);
+
+ // Stop all providers.
+ for (int i = mProviders.size() - 1; i >= 0; i--) {
+ mProviders.get(i).stop();
+ }
+ }
+ }
+
+ private void scanPackages() {
+ if (!mRunning) {
+ return;
+ }
+
+ // Add providers for all new services.
+ // Reorder the list so that providers left at the end will be the ones to remove.
+ int targetIndex = 0;
+ Intent intent = new Intent(MediaRoute2ProviderService.SERVICE_INTERFACE);
+ for (ResolveInfo resolveInfo : mPackageManager.queryIntentServicesAsUser(
+ intent, 0, mUserId)) {
+ ServiceInfo serviceInfo = resolveInfo.serviceInfo;
+ if (serviceInfo != null) {
+ int sourceIndex = findProvider(serviceInfo.packageName, serviceInfo.name);
+ if (sourceIndex < 0) {
+ MediaRoute2ProviderProxy provider =
+ new MediaRoute2ProviderProxy(mContext,
+ new ComponentName(serviceInfo.packageName, serviceInfo.name),
+ mUserId);
+ provider.start();
+ mProviders.add(targetIndex++, provider);
+ mCallback.addProvider(provider);
+ } else if (sourceIndex >= targetIndex) {
+ MediaRoute2ProviderProxy provider = mProviders.get(sourceIndex);
+ provider.start(); // restart the provider if needed
+ provider.rebindIfDisconnected();
+ Collections.swap(mProviders, sourceIndex, targetIndex++);
+ }
+ }
+ }
+
+ // Remove providers for missing services.
+ if (targetIndex < mProviders.size()) {
+ for (int i = mProviders.size() - 1; i >= targetIndex; i--) {
+ MediaRoute2ProviderProxy provider = mProviders.get(i);
+ mCallback.removeProvider(provider);
+ mProviders.remove(provider);
+ provider.stop();
+ }
+ }
+ }
+
+ private int findProvider(String packageName, String className) {
+ int count = mProviders.size();
+ for (int i = 0; i < count; i++) {
+ MediaRoute2ProviderProxy provider = mProviders.get(i);
+ if (provider.hasComponentName(packageName, className)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ private final BroadcastReceiver mScanPackagesReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (DEBUG) {
+ Slog.d(TAG, "Received package manager broadcast: " + intent);
+ }
+ scanPackages();
+ }
+ };
+
+ private final Runnable mScanPackagesRunnable = new Runnable() {
+ @Override
+ public void run() {
+ scanPackages();
+ }
+ };
+
+ public interface Callback {
+ void addProvider(MediaRoute2ProviderProxy provider);
+ void removeProvider(MediaRoute2ProviderProxy provider);
+ }
+}
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index 3eb7321..f822e82 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -16,14 +16,10 @@
package com.android.server.media;
-import com.android.internal.util.DumpUtils;
-import com.android.server.Watchdog;
-
import android.annotation.NonNull;
import android.app.ActivityManager;
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothProfile;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -34,6 +30,7 @@
import android.media.AudioSystem;
import android.media.IAudioRoutesObserver;
import android.media.IAudioService;
+import android.media.IMediaRouter2ManagerClient;
import android.media.IMediaRouterClient;
import android.media.IMediaRouterService;
import android.media.MediaRouter;
@@ -53,10 +50,14 @@
import android.util.ArrayMap;
import android.util.IntArray;
import android.util.Log;
+import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
+import com.android.internal.util.DumpUtils;
+import com.android.server.Watchdog;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -97,6 +98,7 @@
private final Object mLock = new Object();
private final SparseArray<UserRecord> mUserRecords = new SparseArray<>();
private final ArrayMap<IBinder, ClientRecord> mAllClientRecords = new ArrayMap<>();
+ private final ArrayMap<IBinder, ManagerRecord> mAllManagerRecords = new ArrayMap<>();
private int mCurrentUserId = -1;
private final IAudioService mAudioService;
private final AudioPlayerStateMonitor mAudioPlayerStateMonitor;
@@ -306,6 +308,22 @@
// Binder call
@Override
+ public void setControlCategories(IMediaRouterClient client, List<String> categories) {
+ if (client == null) {
+ throw new IllegalArgumentException("client must not be null");
+ }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ setControlCategoriesLocked(client, categories);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ // Binder call
+ @Override
public void setDiscoveryRequest(IMediaRouterClient client,
int routeTypes, boolean activeScan) {
if (client == null) {
@@ -404,6 +422,65 @@
}
}
+ // Binder call
+ @Override
+ public void registerManagerAsUser(IMediaRouter2ManagerClient client,
+ String packageName, int userId) {
+ if (client == null) {
+ throw new IllegalArgumentException("client must not be null");
+ }
+ //TODO: should check permission
+ final boolean trusted = true;
+
+ final int uid = Binder.getCallingUid();
+ if (!validatePackageName(uid, packageName)) {
+ throw new SecurityException("packageName must match the calling uid");
+ }
+
+ final int pid = Binder.getCallingPid();
+ final int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
+ false /*allowAll*/, true /*requireFull*/, "registerManagerAsUser", packageName);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ registerManagerLocked(client, uid, pid, packageName, resolvedUserId, trusted);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ // Binder call
+ @Override
+ public void unregisterManager(IMediaRouter2ManagerClient client) {
+ if (client == null) {
+ throw new IllegalArgumentException("client must not be null");
+ }
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ unregisterManagerLocked(client, false);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ // Binder call
+ @Override
+ public void setRemoteRoute(IMediaRouter2ManagerClient client,
+ int uid, String routeId, boolean explicit) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ setRemoteRouteLocked(client, uid, routeId, explicit);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
void restoreBluetoothA2dp() {
try {
boolean a2dpOn;
@@ -475,6 +552,12 @@
}
}
+ void clientDied(ManagerRecord managerRecord) {
+ synchronized (mLock) {
+ unregisterManagerLocked(managerRecord.mClient, true);
+ }
+ }
+
private void registerClientLocked(IMediaRouterClient client,
int uid, int pid, String packageName, int userId, boolean trusted) {
final IBinder binder = client.asBinder();
@@ -522,6 +605,17 @@
return null;
}
+ private void setControlCategoriesLocked(IMediaRouterClient client, List<String> categories) {
+ final IBinder binder = client.asBinder();
+ ClientRecord clientRecord = mAllClientRecords.get(binder);
+
+ if (clientRecord != null) {
+ clientRecord.mControlCategories = categories;
+ clientRecord.mUserRecord.mHandler.obtainMessage(
+ UserHandler.MSG_UPDATE_CLIENT_USAGE, clientRecord).sendToTarget();
+ }
+ }
+
private void setDiscoveryRequestLocked(IMediaRouterClient client,
int routeTypes, boolean activeScan) {
final IBinder binder = client.asBinder();
@@ -575,6 +669,63 @@
}
}
+ private void registerManagerLocked(IMediaRouter2ManagerClient client,
+ int uid, int pid, String packageName, int userId, boolean trusted) {
+ final IBinder binder = client.asBinder();
+ ManagerRecord managerRecord = mAllManagerRecords.get(binder);
+ if (managerRecord == null) {
+ boolean newUser = false;
+ UserRecord userRecord = mUserRecords.get(userId);
+ if (userRecord == null) {
+ userRecord = new UserRecord(userId);
+ newUser = true;
+ }
+ managerRecord = new ManagerRecord(userRecord, client, uid, pid, packageName, trusted);
+ try {
+ binder.linkToDeath(managerRecord, 0);
+ } catch (RemoteException ex) {
+ throw new RuntimeException("Media router client died prematurely.", ex);
+ }
+
+ if (newUser) {
+ mUserRecords.put(userId, userRecord);
+ initializeUserLocked(userRecord);
+ }
+
+ userRecord.mManagerRecords.add(managerRecord);
+ mAllManagerRecords.put(binder, managerRecord);
+
+ // send client usage to manager
+ final int clientCount = userRecord.mClientRecords.size();
+ for (int i = 0; i < clientCount; i++) {
+ userRecord.mHandler.obtainMessage(UserHandler.MSG_UPDATE_CLIENT_USAGE,
+ userRecord.mClientRecords.get(i)).sendToTarget();
+ }
+ }
+ }
+
+ private void unregisterManagerLocked(IMediaRouter2ManagerClient client, boolean died) {
+ ManagerRecord clientRecord = mAllManagerRecords.remove(client.asBinder());
+ if (clientRecord != null) {
+ UserRecord userRecord = clientRecord.mUserRecord;
+ userRecord.mManagerRecords.remove(clientRecord);
+ clientRecord.dispose();
+ disposeUserIfNeededLocked(userRecord); // since client removed from user
+ }
+ }
+
+ private void setRemoteRouteLocked(IMediaRouter2ManagerClient client,
+ int uid, String routeId, boolean explicit) {
+ ManagerRecord managerRecord = mAllManagerRecords.get(client.asBinder());
+ if (managerRecord != null) {
+ if (explicit && managerRecord.mTrusted) {
+ Pair<Integer, String> obj = new Pair<>(uid, routeId);
+ managerRecord.mUserRecord.mHandler.obtainMessage(
+ UserHandler.MSG_SELECT_REMOTE_ROUTE, obj).sendToTarget();
+ }
+ }
+ }
+
private void requestSetVolumeLocked(IMediaRouterClient client,
String routeId, int volume) {
final IBinder binder = client.asBinder();
@@ -667,6 +818,46 @@
}
}
+ final class ManagerRecord implements DeathRecipient {
+ public final UserRecord mUserRecord;
+ public final IMediaRouter2ManagerClient mClient;
+ public final int mUid;
+ public final int mPid;
+ public final String mPackageName;
+ public final boolean mTrusted;
+
+ ManagerRecord(UserRecord userRecord, IMediaRouter2ManagerClient client,
+ int uid, int pid, String packageName, boolean trusted) {
+ mUserRecord = userRecord;
+ mClient = client;
+ mUid = uid;
+ mPid = pid;
+ mPackageName = packageName;
+ mTrusted = trusted;
+ }
+
+ public void dispose() {
+ mClient.asBinder().unlinkToDeath(this, 0);
+ }
+
+ @Override
+ public void binderDied() {
+ clientDied(this);
+ }
+
+ public void dump(PrintWriter pw, String prefix) {
+ pw.println(prefix + this);
+
+ final String indent = prefix + " ";
+ pw.println(indent + "mTrusted=" + mTrusted);
+ }
+
+ @Override
+ public String toString() {
+ return "Client " + mPackageName + " (pid " + mPid + ")";
+ }
+ }
+
/**
* Information about a particular client of the media router.
* The contents of this object is guarded by mLock.
@@ -678,6 +869,7 @@
public final int mPid;
public final String mPackageName;
public final boolean mTrusted;
+ public List<String> mControlCategories;
public int mRouteTypes;
public boolean mActiveScan;
@@ -728,7 +920,8 @@
*/
final class UserRecord {
public final int mUserId;
- public final ArrayList<ClientRecord> mClientRecords = new ArrayList<ClientRecord>();
+ public final ArrayList<ClientRecord> mClientRecords = new ArrayList<>();
+ public final ArrayList<ManagerRecord> mManagerRecords = new ArrayList<>();
public final UserHandler mHandler;
public MediaRouterClientState mRouterState;
@@ -783,7 +976,9 @@
*/
static final class UserHandler extends Handler
implements RemoteDisplayProviderWatcher.Callback,
- RemoteDisplayProviderProxy.Callback {
+ RemoteDisplayProviderProxy.Callback,
+ MediaRoute2ProviderWatcher.Callback,
+ MediaRoute2ProviderProxy.Callback {
public static final int MSG_START = 1;
public static final int MSG_STOP = 2;
public static final int MSG_UPDATE_DISCOVERY_REQUEST = 3;
@@ -794,6 +989,9 @@
private static final int MSG_UPDATE_CLIENT_STATE = 8;
private static final int MSG_CONNECTION_TIMED_OUT = 9;
+ private static final int MSG_SELECT_REMOTE_ROUTE = 10;
+ private static final int MSG_UPDATE_CLIENT_USAGE = 11;
+
private static final int TIMEOUT_REASON_NOT_AVAILABLE = 1;
private static final int TIMEOUT_REASON_CONNECTION_LOST = 2;
private static final int TIMEOUT_REASON_WAITING_FOR_CONNECTING = 3;
@@ -809,11 +1007,17 @@
private final MediaRouterService mService;
private final UserRecord mUserRecord;
private final RemoteDisplayProviderWatcher mWatcher;
+ private final MediaRoute2ProviderWatcher mMediaWatcher;
+
private final ArrayList<ProviderRecord> mProviderRecords =
new ArrayList<ProviderRecord>();
private final ArrayList<IMediaRouterClient> mTempClients =
new ArrayList<IMediaRouterClient>();
+ private final ArrayList<MediaRoute2ProviderProxy> mMediaProviders =
+ new ArrayList<>();
+ private final ArrayList<IMediaRouter2ManagerClient> mTempManagers = new ArrayList<>();
+
private boolean mRunning;
private int mDiscoveryMode = RemoteDisplayState.DISCOVERY_MODE_NONE;
private RouteRecord mSelectedRouteRecord;
@@ -828,6 +1032,8 @@
mUserRecord = userRecord;
mWatcher = new RemoteDisplayProviderWatcher(service.mContext, this,
this, mUserRecord.mUserId);
+ mMediaWatcher = new MediaRoute2ProviderWatcher(service.mContext, this,
+ this, mUserRecord.mUserId);
}
@Override
@@ -869,6 +1075,15 @@
connectionTimedOut();
break;
}
+ case MSG_SELECT_REMOTE_ROUTE: {
+ Pair<Integer, String> obj = (Pair<Integer, String>) msg.obj;
+ selectRemoteRoute(obj.first, obj.second);
+ break;
+ }
+ case MSG_UPDATE_CLIENT_USAGE: {
+ updateClientUsage((ClientRecord) msg.obj);
+ break;
+ }
}
}
@@ -900,6 +1115,7 @@
if (!mRunning) {
mRunning = true;
mWatcher.start(); // also starts all providers
+ mMediaWatcher.start();
}
}
@@ -908,6 +1124,7 @@
mRunning = false;
unselectSelectedRoute();
mWatcher.stop(); // also stops all providers
+ mMediaWatcher.stop();
}
}
@@ -1039,6 +1256,26 @@
}
}
+ @Override
+ public void addProvider(MediaRoute2ProviderProxy provider) {
+ provider.setCallback(this);
+ mMediaProviders.add(provider);
+ }
+
+ @Override
+ public void removeProvider(MediaRoute2ProviderProxy provider) {
+ mMediaProviders.remove(provider);
+ }
+
+ @Override
+ public void onProviderStateChanged(MediaRoute2ProviderProxy provider) {
+ updateProvider(provider);
+ }
+
+ private void updateProvider(MediaRoute2ProviderProxy provider) {
+ scheduleUpdateClientState();
+ }
+
/**
* This function is called whenever the state of the selected route may have changed.
* It checks the state and updates timeouts or unselects the route as appropriate.
@@ -1149,6 +1386,17 @@
unselectSelectedRoute();
}
+ private void selectRemoteRoute(int uid, String routeId) {
+ if (routeId != null) {
+ final int providerCount = mMediaProviders.size();
+
+ //TODO: should find proper provider (currently assumes a single provider)
+ for (int i = 0; i < providerCount; ++i) {
+ mMediaProviders.get(i).setSelectedRoute(uid, routeId);
+ }
+ }
+ }
+
private void scheduleUpdateClientState() {
if (!mClientStateUpdateScheduled) {
mClientStateUpdateScheduled = true;
@@ -1166,6 +1414,15 @@
mProviderRecords.get(i).appendClientState(routerState);
}
+ //TODO: send provider info
+ int selectedUid = 0;
+ String selectedRouteId = null;
+ final int mediaCount = mMediaProviders.size();
+ for (int i = 0; i < mediaCount; i++) {
+ selectedUid = mMediaProviders.get(i).mSelectedUid;
+ selectedRouteId = mMediaProviders.get(i).mSelectedRouteId;
+ }
+
try {
synchronized (mService.mLock) {
// Update the UserRecord.
@@ -1176,6 +1433,11 @@
for (int i = 0; i < count; i++) {
mTempClients.add(mUserRecord.mClientRecords.get(i).mClient);
}
+
+ final int count2 = mUserRecord.mManagerRecords.size();
+ for (int i = 0; i < count2; i++) {
+ mTempManagers.add(mUserRecord.mManagerRecords.get(i).mClient);
+ }
}
// Notify all clients (outside of the lock).
@@ -1187,9 +1449,39 @@
Slog.w(TAG, "Failed to call onStateChanged. Client probably died.");
}
}
+ //TODO: Call proper callbacks when provider descriptor is implemented.
+ final int count2 = mTempManagers.size();
+ for (int i = 0; i < count2; i++) {
+ try {
+ mTempManagers.get(i).onRouteSelected(selectedUid, selectedRouteId);
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "Failed to call onStateChanged. Manager probably died.", ex);
+ }
+ }
} finally {
// Clear the list in preparation for the next time.
mTempClients.clear();
+ mTempManagers.clear();
+ }
+ }
+
+ private void updateClientUsage(ClientRecord clientRecord) {
+ List<IMediaRouter2ManagerClient> managers = new ArrayList<>();
+ synchronized (mService.mLock) {
+ final int count = mUserRecord.mManagerRecords.size();
+ for (int i = 0; i < count; i++) {
+ managers.add(mUserRecord.mManagerRecords.get(i).mClient);
+ }
+ }
+ final int count = managers.size();
+ for (int i = 0; i < count; i++) {
+ try {
+ managers.get(i).onControlCategoriesChanged(clientRecord.mUid,
+ clientRecord.mControlCategories);
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "Failed to call onControlCategoriesChanged. "
+ + "Manager probably died.", ex);
+ }
}
}
@@ -1576,4 +1868,5 @@
}
}
}
+
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index e43fc1f..dddb7ef 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -4106,11 +4106,19 @@
if (r == null) {
return;
}
- if (mAssistants.isAdjustmentAllowed(adjustment.getKey())) {
- if (adjustment.getSignals() != null) {
- Bundle.setDefusable(adjustment.getSignals(), true);
- r.addAdjustment(adjustment);
+ if (adjustment.getSignals() != null) {
+ final Bundle adjustments = adjustment.getSignals();
+ Bundle.setDefusable(adjustments, true);
+ List<String> toRemove = new ArrayList<>();
+ for (String potentialKey : adjustments.keySet()) {
+ if (!mAssistants.isAdjustmentAllowed(potentialKey)) {
+ toRemove.add(potentialKey);
+ }
}
+ for (String removeKey : toRemove) {
+ adjustments.remove(removeKey);
+ }
+ r.addAdjustment(adjustment);
}
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 66b530f..f1d4524 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -476,6 +476,7 @@
mResolvedBaseFile.getAbsolutePath() : null;
info.progress = mProgress;
info.sealed = mSealed;
+ info.isCommitted = mCommitted;
info.active = mActiveCount.get() > 0;
info.mode = params.mode;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 73b000e..1a33b16 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1141,11 +1141,22 @@
switch (userStatus) {
case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS:
if (!verified) {
- // updatedStatus is already UNDEFINED
- needUpdate = true;
+ // Don't demote if sysconfig says 'always'
+ SystemConfig systemConfig = SystemConfig.getInstance();
+ ArraySet<String> packages = systemConfig.getLinkedApps();
+ if (!packages.contains(packageName)) {
+ // updatedStatus is already UNDEFINED
+ needUpdate = true;
- if (DEBUG_DOMAIN_VERIFICATION) {
- Slog.d(TAG, "Formerly validated but now failing; demoting");
+ if (DEBUG_DOMAIN_VERIFICATION) {
+ Slog.d(TAG, "Formerly validated but now failing; demoting");
+ }
+ } else {
+ if (DEBUG_DOMAIN_VERIFICATION) {
+ Slog.d(TAG, "Updating bundled package " + packageName
+ + " failed autoVerify, but sysconfig supersedes");
+ }
+ // leave needUpdate == false here intentionally
}
}
break;
@@ -17789,6 +17800,12 @@
@Override
public boolean isPackageDeviceAdminOnAnyUser(String packageName) {
final int callingUid = Binder.getCallingUid();
+ if (checkUidPermission(android.Manifest.permission.MANAGE_USERS, callingUid)
+ != PERMISSION_GRANTED) {
+ EventLog.writeEvent(0x534e4554, "128599183", -1, "");
+ throw new SecurityException(android.Manifest.permission.MANAGE_USERS
+ + " permission is required to call this API");
+ }
if (getInstantAppPackageName(callingUid) != null
&& !isCallerSameApp(packageName, callingUid)) {
return false;
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 0a17e130..212df43 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -189,11 +189,8 @@
@Deprecated
private static final Set<String> STORAGE_PERMISSIONS = new ArraySet<>();
static {
- // STOPSHIP(b/112545973): remove once feature enabled by default
- if (!StorageManager.hasIsolatedStorage()) {
- STORAGE_PERMISSIONS.add(Manifest.permission.READ_EXTERNAL_STORAGE);
- STORAGE_PERMISSIONS.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
- }
+ STORAGE_PERMISSIONS.add(Manifest.permission.READ_EXTERNAL_STORAGE);
+ STORAGE_PERMISSIONS.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
}
private static final Set<String> MEDIA_AURAL_PERMISSIONS = new ArraySet<>();
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 55af357..1c5c7a3 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -3807,6 +3807,9 @@
}
case KeyEvent.KEYCODE_POWER: {
+ Slog.d(TAG, "interceptKeyBeforeQueueing: KEYCODE_POWER "
+ + KeyEvent.actionToString(event.getAction())
+ + " mPowerKeyHandled=" + mPowerKeyHandled + " b/128933363");
// Any activity on the power button stops the accessibility shortcut
cancelPendingAccessibilityShortcutAction();
result &= ~ACTION_PASS_TO_USER;
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 1800433..28a853f 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -1724,7 +1724,7 @@
throw new IllegalStateException("mKernelCpuThreadReader is null");
}
ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processCpuUsages =
- this.mKernelCpuThreadReader.getProcessCpuUsageByUids();
+ this.mKernelCpuThreadReader.getProcessCpuUsage();
if (processCpuUsages == null) {
throw new IllegalStateException("processCpuUsages is null");
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 426122a..987d46a 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -40,6 +40,8 @@
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.Build.VERSION_CODES.O_MR1;
import static android.os.Build.VERSION_CODES.P;
+import static android.service.notification.Adjustment.KEY_IMPORTANCE;
+import static android.service.notification.Adjustment.KEY_USER_SENTIMENT;
import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL;
@@ -492,6 +494,19 @@
return answers;
}
+ private void clearDeviceConfig() {
+ DeviceConfig.resetToDefaults(
+ Settings.RESET_MODE_PACKAGE_DEFAULTS, DeviceConfig.NAMESPACE_SYSTEMUI);
+ }
+
+ private void setDefaultAssistantInDeviceConfig(String componentName) {
+ DeviceConfig.setProperty(
+ DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.NAS_DEFAULT_SERVICE,
+ componentName,
+ false);
+ }
+
@Test
public void testCreateNotificationChannels_SingleChannel() throws Exception {
final NotificationChannel channel =
@@ -831,7 +846,7 @@
mService.addEnqueuedNotification(r);
Bundle bundle = new Bundle();
- bundle.putInt(Adjustment.KEY_IMPORTANCE, IMPORTANCE_NONE);
+ bundle.putInt(KEY_IMPORTANCE, IMPORTANCE_NONE);
Adjustment adjustment = new Adjustment(
r.sbn.getPackageName(), r.getKey(), bundle, "", r.getUser().getIdentifier());
mBinderService.applyEnqueuedAdjustmentFromAssistant(null, adjustment);
@@ -2826,7 +2841,7 @@
mService.setHandler(handler);
Bundle signals = new Bundle();
- signals.putInt(Adjustment.KEY_IMPORTANCE, IMPORTANCE_NONE);
+ signals.putInt(KEY_IMPORTANCE, IMPORTANCE_NONE);
Adjustment adjustment = new Adjustment(
r.sbn.getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
@@ -2867,7 +2882,7 @@
when(mAssistants.isSameUser(eq(null), anyInt())).thenReturn(true);
Bundle signals = new Bundle();
- signals.putInt(Adjustment.KEY_IMPORTANCE, IMPORTANCE_LOW);
+ signals.putInt(KEY_IMPORTANCE, IMPORTANCE_LOW);
Adjustment adjustment = new Adjustment(
r.sbn.getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
mBinderService.applyEnqueuedAdjustmentFromAssistant(null, adjustment);
@@ -2885,13 +2900,13 @@
when(mAssistants.isSameUser(eq(null), anyInt())).thenReturn(true);
Bundle signals = new Bundle();
- signals.putInt(Adjustment.KEY_IMPORTANCE, IMPORTANCE_LOW);
+ signals.putInt(KEY_IMPORTANCE, IMPORTANCE_LOW);
Adjustment adjustment = new Adjustment(
r.sbn.getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
mBinderService.applyEnqueuedAdjustmentFromAssistant(null, adjustment);
assertEquals(IMPORTANCE_DEFAULT, r.getImportance());
- assertFalse(r.hasAdjustment(Adjustment.KEY_IMPORTANCE));
+ assertFalse(r.hasAdjustment(KEY_IMPORTANCE));
}
@Test
@@ -4275,18 +4290,7 @@
.onGranted(eq(xmlConfig), eq(0), eq(true));
}
- private void clearDeviceConfig() {
- DeviceConfig.resetToDefaults(
- Settings.RESET_MODE_PACKAGE_DEFAULTS, DeviceConfig.NAMESPACE_SYSTEMUI);
- }
- private void setDefaultAssistantInDeviceConfig(String componentName) {
- DeviceConfig.setProperty(
- DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.NAS_DEFAULT_SERVICE,
- componentName,
- false);
- }
public void testGetAllowedAssistantCapabilities() throws Exception {
List<String> capabilities = mBinderService.getAllowedAssistantCapabilities(null);
@@ -4301,4 +4305,23 @@
assertFalse(currentCapabilities.contains(capability));
}
}
+
+ public void testAdjustRestrictedKey() throws Exception {
+ NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+
+ when(mAssistants.isAdjustmentAllowed(KEY_IMPORTANCE)).thenReturn(true);
+ when(mAssistants.isAdjustmentAllowed(KEY_USER_SENTIMENT)).thenReturn(false);
+
+ Bundle signals = new Bundle();
+ signals.putInt(KEY_IMPORTANCE, IMPORTANCE_LOW);
+ signals.putInt(KEY_USER_SENTIMENT, USER_SENTIMENT_NEGATIVE);
+ Adjustment adjustment = new Adjustment(r.sbn.getPackageName(), r.getKey(), signals,
+ "", r.getUser().getIdentifier());
+
+ mBinderService.applyAdjustmentFromAssistant(null, adjustment);
+ r.applyAdjustments();
+
+ assertEquals(IMPORTANCE_LOW, r.getAssistantImportance());
+ assertEquals(USER_SENTIMENT_NEUTRAL, r.getUserSentiment());
+ }
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 5df7bf7..d5245e3 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1321,7 +1321,7 @@
/**
* An int extra used with {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} to indicate the
- * subscription which has changed.
+ * subscription which has changed; or in general whenever a subscription ID needs specified.
*/
public static final String EXTRA_SUBSCRIPTION_ID = "android.telephony.extra.SUBSCRIPTION_ID";
@@ -1439,13 +1439,24 @@
/**
* Integer intent extra to be used with {@link #ACTION_PRIMARY_SUBSCRIPTION_LIST_CHANGED}
- * to indicate whether a SIM selection is needed to choose default subscription.
+ * to indicate what type of SIM selection is needed.
*
* @hide
*/
public static final String EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE =
"android.telephony.extra.DEFAULT_SUBSCRIPTION_SELECT_TYPE";
+ /** @hide */
+ @IntDef({
+ EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_NONE,
+ EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DATA,
+ EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_VOICE,
+ EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_SMS,
+ EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_ALL
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DefaultSubscriptionSelectType{}
+
/**
* Used as an int value for {@link #EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE}
* to indicate there's no need to re-select any default subscription.
@@ -1477,20 +1488,11 @@
/**
* Used as an int value for {@link #EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE}
* to indicate user to decide whether current SIM should be preferred for all
- * data / voice / sms.
+ * data / voice / sms. {@link #EXTRA_SUBSCRIPTION_ID} will specified to indicate
+ * which subscription should be the default subscription.
* @hide
*/
- public static final int EXTRA_DEFAULT_SUBSCRIPTION_SELECT_FOR_ALL_TYPES = 4;
-
- /**
- * Integer intent extra to be used with
- * {@link #EXTRA_DEFAULT_SUBSCRIPTION_SELECT_FOR_ALL_TYPES}
- * to indicate which SIM is being selected.
- *
- * @hide
- */
- public static final String EXTRA_DEFAULT_SUBSCRIPTION_ID =
- "android.telephony.extra.DEFAULT_SUBSCRIPTION_ID";
+ public static final int EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_ALL = 4;
//
//
diff --git a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
index 4886a3f..d93e582 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
@@ -123,6 +123,19 @@
context, TELEPHONY_SUPPLIER, subId, pid, uid, callingPackage, message);
}
+ /**
+ * Check whether the calling packages has carrier privileges for the passing subscription.
+ * @return {@code true} if the caller has carrier privileges, {@false} otherwise.
+ */
+ public static boolean checkCarrierPrivilegeForSubId(int subId) {
+ if (SubscriptionManager.isValidSubscriptionId(subId)
+ && getCarrierPrivilegeStatus(TELEPHONY_SUPPLIER, subId, Binder.getCallingUid())
+ == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
+ return true;
+ }
+ return false;
+ }
+
@VisibleForTesting
public static boolean checkReadPhoneState(
Context context, Supplier<ITelephony> telephonySupplier, int subId, int pid, int uid,
@@ -204,9 +217,7 @@
}
// Calling packages with carrier privileges will also have access to device identifiers, but
// this may be removed in a future release.
- if (SubscriptionManager.isValidSubscriptionId(subId) && getCarrierPrivilegeStatus(
- TELEPHONY_SUPPLIER, subId, uid)
- == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
+ if (checkCarrierPrivilegeForSubId(subId)) {
return true;
}
// else the calling package is not authorized to access the device identifiers; call
@@ -243,9 +254,7 @@
}
// If the calling package has carrier privileges then allow access to the subscriber
// identifiers.
- if (SubscriptionManager.isValidSubscriptionId(subId) && getCarrierPrivilegeStatus(
- TELEPHONY_SUPPLIER, subId, uid)
- == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
+ if (checkCarrierPrivilegeForSubId(subId)) {
return true;
}
return reportAccessDeniedToReadIdentifiers(context, subId, pid, uid, callingPackage,
@@ -365,9 +374,7 @@
uid) == PackageManager.PERMISSION_GRANTED) {
return false;
}
- if (SubscriptionManager.isValidSubscriptionId(subId)
- && getCarrierPrivilegeStatus(TELEPHONY_SUPPLIER, subId, uid)
- == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
+ if (checkCarrierPrivilegeForSubId(subId)) {
return false;
}
}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 1151214..e881ad4 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -212,7 +212,6 @@
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.function.Consumer;
import java.util.function.Predicate;
/**
@@ -4022,8 +4021,13 @@
callback3.expectStopped();
}
+ @FunctionalInterface
+ private interface ThrowingConsumer<T> {
+ void accept(T t) throws Exception;
+ }
+
// Helper method to prepare the executor and run test
- private void runTestWithSerialExecutors(Consumer<Executor> functor) {
+ private void runTestWithSerialExecutors(ThrowingConsumer<Executor> functor) throws Exception {
final ExecutorService executorSingleThread = Executors.newSingleThreadExecutor();
final Executor executorInline = (Runnable r) -> r.run();
functor.accept(executorSingleThread);
@@ -4032,15 +4036,9 @@
}
@Test
- public void testNattSocketKeepalives() {
- runTestWithSerialExecutors(executor -> {
- try {
- doTestNattSocketKeepalivesWithExecutor(executor);
- doTestNattSocketKeepalivesFdWithExecutor(executor);
- } catch (Exception e) {
- fail(e.getMessage());
- }
- });
+ public void testNattSocketKeepalives() throws Exception {
+ runTestWithSerialExecutors(executor -> doTestNattSocketKeepalivesWithExecutor(executor));
+ runTestWithSerialExecutors(executor -> doTestNattSocketKeepalivesFdWithExecutor(executor));
}
private void doTestNattSocketKeepalivesWithExecutor(Executor executor) throws Exception {
@@ -4210,14 +4208,8 @@
}
@Test
- public void testTcpSocketKeepalives() {
- runTestWithSerialExecutors(executor -> {
- try {
- doTestTcpSocketKeepalivesWithExecutor(executor);
- } catch (Exception e) {
- fail(e.getMessage());
- }
- });
+ public void testTcpSocketKeepalives() throws Exception {
+ runTestWithSerialExecutors(executor -> doTestTcpSocketKeepalivesWithExecutor(executor));
}
private void doTestTcpSocketKeepalivesWithExecutor(Executor executor) throws Exception {
diff --git a/tools/bit/main.cpp b/tools/bit/main.cpp
index 0f555b1..1a91f52 100644
--- a/tools/bit/main.cpp
+++ b/tools/bit/main.cpp
@@ -856,7 +856,7 @@
// TODO: if (!apk.file.fileInfo.exists || apk.file.HasChanged())
err = run_adb("shell", "mkdir", "-p", dir.c_str(), NULL);
check_error(err);
- err = run_adb("push", pushed.file.filename.c_str(), pushed.dest.c_str());
+ err = run_adb("push", pushed.file.filename.c_str(), pushed.dest.c_str(), NULL);
check_error(err);
// pushed.installed = true;
}