Merge changes from topic "downloadvuln"
* changes:
Execute "strict" queries with extra parentheses.
Add support for appending standalone phrases.
diff --git a/Android.bp b/Android.bp
index 694bf26..8c81534 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1185,6 +1185,7 @@
"core/java/overview.html",
":current-support-api",
],
+ dex_api_filename: "public-dex.txt",
private_dex_api_filename: "private-dex.txt",
removed_dex_api_filename: "removed-dex.txt",
args: framework_docs_args +
diff --git a/Android.mk b/Android.mk
index 031809c..7890983 100644
--- a/Android.mk
+++ b/Android.mk
@@ -712,6 +712,7 @@
LOCAL_SRC_GREYLIST := frameworks/base/config/hiddenapi-light-greylist.txt
LOCAL_SRC_VENDOR_LIST := frameworks/base/config/hiddenapi-vendor-list.txt
LOCAL_SRC_FORCE_BLACKLIST := frameworks/base/config/hiddenapi-force-blacklist.txt
+LOCAL_SRC_PUBLIC_API := $(INTERNAL_PLATFORM_DEX_API_FILE)
LOCAL_SRC_PRIVATE_API := $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE)
LOCAL_SRC_REMOVED_API := $(INTERNAL_PLATFORM_REMOVED_DEX_API_FILE)
@@ -719,6 +720,7 @@
$(LOCAL_SRC_GREYLIST) \
$(LOCAL_SRC_VENDOR_LIST) \
$(LOCAL_SRC_FORCE_BLACKLIST) \
+ $(LOCAL_SRC_PUBLIC_API) \
$(LOCAL_SRC_PRIVATE_API) \
$(LOCAL_SRC_REMOVED_API)
@@ -791,7 +793,8 @@
# (4) subtract entries shared with LOCAL_LIGHT_GREYLIST
$(LOCAL_DARK_GREYLIST): $(LOCAL_SRC_ALL) $(LOCAL_LIGHT_GREYLIST)
comm -13 <(sort $(LOCAL_LIGHT_GREYLIST) $(LOCAL_SRC_FORCE_BLACKLIST)) \
- <(sed 's/\->.*//' $(LOCAL_LIGHT_GREYLIST) | sed 's/\(.*\/\).*/\1/' | sort | uniq | \
+ <(cat $(LOCAL_SRC_PUBLIC_API) $(LOCAL_LIGHT_GREYLIST) | \
+ sed 's/\->.*//' | sed 's/\(.*\/\).*/\1/' | sort | uniq | \
while read PKG_NAME; do \
grep -E "^$${PKG_NAME}[^/;]*;" $(LOCAL_SRC_PRIVATE_API); \
done | sort | uniq) \
diff --git a/api/system-current.txt b/api/system-current.txt
index 7c0d958..924d3af 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5180,8 +5180,8 @@
method public int[] getAvailableServices();
method public android.telephony.CellIdentity getCellIdentity();
method public int getDomain();
- method public int getReasonForDenial();
method public int getRegState();
+ method public int getRejectCause();
method public int getTransportType();
method public boolean isEmergencyEnabled();
method public void writeToParcel(android.os.Parcel, int);
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index 89b1f41..6984aa2 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -33,7 +33,7 @@
namespace statsd {
struct GaugeAtom {
- GaugeAtom(std::shared_ptr<vector<FieldValue>> fields, int64_t elapsedTimeNs, int wallClockNs)
+ GaugeAtom(std::shared_ptr<vector<FieldValue>> fields, int64_t elapsedTimeNs, int64_t wallClockNs)
: mFields(fields), mElapsedTimestamps(elapsedTimeNs), mWallClockTimestampNs(wallClockNs) {
}
std::shared_ptr<vector<FieldValue>> mFields;
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index f27b286..3f579bc 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -3080,16 +3080,32 @@
*/
public int processState;
+ /**
+ * Whether the app is focused in multi-window environment.
+ * @hide
+ */
+ public boolean isFocused;
+
+ /**
+ * Copy of {@link com.android.server.am.ProcessRecord#lastActivityTime} of the process.
+ * @hide
+ */
+ public long lastActivityTime;
+
public RunningAppProcessInfo() {
importance = IMPORTANCE_FOREGROUND;
importanceReasonCode = REASON_UNKNOWN;
processState = PROCESS_STATE_IMPORTANT_FOREGROUND;
+ isFocused = false;
+ lastActivityTime = 0;
}
public RunningAppProcessInfo(String pProcessName, int pPid, String pArr[]) {
processName = pProcessName;
pid = pPid;
pkgList = pArr;
+ isFocused = false;
+ lastActivityTime = 0;
}
public int describeContents() {
@@ -3110,6 +3126,8 @@
ComponentName.writeToParcel(importanceReasonComponent, dest);
dest.writeInt(importanceReasonImportance);
dest.writeInt(processState);
+ dest.writeInt(isFocused ? 1 : 0);
+ dest.writeLong(lastActivityTime);
}
public void readFromParcel(Parcel source) {
@@ -3126,6 +3144,8 @@
importanceReasonComponent = ComponentName.readFromParcel(source);
importanceReasonImportance = source.readInt();
processState = source.readInt();
+ isFocused = source.readInt() != 0;
+ lastActivityTime = source.readLong();
}
public static final Creator<RunningAppProcessInfo> CREATOR =
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index cba7003..3dbc312 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -128,7 +128,7 @@
in boolean stopProfiling);
void activityResumed(in IBinder token);
void activityPaused(in IBinder token);
- oneway void activityStopped(in IBinder token, in Bundle state,
+ void activityStopped(in IBinder token, in Bundle state,
in PersistableBundle persistentState, in CharSequence description);
oneway void activityDestroyed(in IBinder token);
void activityRelaunched(in IBinder token);
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index ab2cf86..175b405 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -21,9 +21,7 @@
import android.util.ExceptionUtils;
import android.util.Log;
import android.util.Slog;
-import android.util.SparseIntArray;
-import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.BinderCallsStats;
import com.android.internal.os.BinderInternal;
import com.android.internal.util.FastPrintWriter;
@@ -36,12 +34,7 @@
import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.PrintWriter;
-import java.lang.ref.WeakReference;
import java.lang.reflect.Modifier;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Map;
/**
* Base class for a remotable object, the core part of a lightweight
@@ -783,442 +776,3 @@
}
}
-/**
- * Java proxy for a native IBinder object.
- * Allocated and constructed by the native javaObjectforIBinder function. Never allocated
- * directly from Java code.
- */
-final class BinderProxy implements IBinder {
- // See android_util_Binder.cpp for the native half of this.
-
- // Assume the process-wide default value when created
- volatile boolean mWarnOnBlocking = Binder.sWarnOnBlocking;
-
- /*
- * Map from longs to BinderProxy, retaining only a WeakReference to the BinderProxies.
- * We roll our own only because we need to lazily remove WeakReferences during accesses
- * to avoid accumulating junk WeakReference objects. WeakHashMap isn't easily usable
- * because we want weak values, not keys.
- * Our hash table is never resized, but the number of entries is unlimited;
- * performance degrades as occupancy increases significantly past MAIN_INDEX_SIZE.
- * Not thread-safe. Client ensures there's a single access at a time.
- */
- private static final class ProxyMap {
- private static final int LOG_MAIN_INDEX_SIZE = 8;
- private static final int MAIN_INDEX_SIZE = 1 << LOG_MAIN_INDEX_SIZE;
- private static final int MAIN_INDEX_MASK = MAIN_INDEX_SIZE - 1;
- // Debuggable builds will throw an AssertionError if the number of map entries exceeds:
- private static final int CRASH_AT_SIZE = 20_000;
-
- /**
- * We next warn when we exceed this bucket size.
- */
- private int mWarnBucketSize = 20;
-
- /**
- * Increment mWarnBucketSize by WARN_INCREMENT each time we warn.
- */
- private static final int WARN_INCREMENT = 10;
-
- /**
- * Hash function tailored to native pointers.
- * Returns a value < MAIN_INDEX_SIZE.
- */
- private static int hash(long arg) {
- return ((int) ((arg >> 2) ^ (arg >> (2 + LOG_MAIN_INDEX_SIZE)))) & MAIN_INDEX_MASK;
- }
-
- /**
- * Return the total number of pairs in the map.
- */
- private int size() {
- int size = 0;
- for (ArrayList<WeakReference<BinderProxy>> a : mMainIndexValues) {
- if (a != null) {
- size += a.size();
- }
- }
- return size;
- }
-
- /**
- * Return the total number of pairs in the map containing values that have
- * not been cleared. More expensive than the above size function.
- */
- private int unclearedSize() {
- int size = 0;
- for (ArrayList<WeakReference<BinderProxy>> a : mMainIndexValues) {
- if (a != null) {
- for (WeakReference<BinderProxy> ref : a) {
- if (ref.get() != null) {
- ++size;
- }
- }
- }
- }
- return size;
- }
-
- /**
- * Remove ith entry from the hash bucket indicated by hash.
- */
- private void remove(int hash, int index) {
- Long[] keyArray = mMainIndexKeys[hash];
- ArrayList<WeakReference<BinderProxy>> valueArray = mMainIndexValues[hash];
- int size = valueArray.size(); // KeyArray may have extra elements.
- // Move last entry into empty slot, and truncate at end.
- if (index != size - 1) {
- keyArray[index] = keyArray[size - 1];
- valueArray.set(index, valueArray.get(size - 1));
- }
- valueArray.remove(size - 1);
- // Just leave key array entry; it's unused. We only trust the valueArray size.
- }
-
- /**
- * Look up the supplied key. If we have a non-cleared entry for it, return it.
- */
- BinderProxy get(long key) {
- int myHash = hash(key);
- Long[] keyArray = mMainIndexKeys[myHash];
- if (keyArray == null) {
- return null;
- }
- ArrayList<WeakReference<BinderProxy>> valueArray = mMainIndexValues[myHash];
- int bucketSize = valueArray.size();
- for (int i = 0; i < bucketSize; ++i) {
- long foundKey = keyArray[i];
- if (key == foundKey) {
- WeakReference<BinderProxy> wr = valueArray.get(i);
- BinderProxy bp = wr.get();
- if (bp != null) {
- return bp;
- } else {
- remove(myHash, i);
- return null;
- }
- }
- }
- return null;
- }
-
- private int mRandom; // A counter used to generate a "random" index. World's 2nd worst RNG.
-
- /**
- * Add the key-value pair to the map.
- * Requires that the indicated key is not already in the map.
- */
- void set(long key, @NonNull BinderProxy value) {
- int myHash = hash(key);
- ArrayList<WeakReference<BinderProxy>> valueArray = mMainIndexValues[myHash];
- if (valueArray == null) {
- valueArray = mMainIndexValues[myHash] = new ArrayList<>();
- mMainIndexKeys[myHash] = new Long[1];
- }
- int size = valueArray.size();
- WeakReference<BinderProxy> newWr = new WeakReference<>(value);
- // First look for a cleared reference.
- // This ensures that ArrayList size is bounded by the maximum occupancy of
- // that bucket.
- for (int i = 0; i < size; ++i) {
- if (valueArray.get(i).get() == null) {
- valueArray.set(i, newWr);
- Long[] keyArray = mMainIndexKeys[myHash];
- keyArray[i] = key;
- if (i < size - 1) {
- // "Randomly" check one of the remaining entries in [i+1, size), so that
- // needlessly long buckets are eventually pruned.
- int rnd = Math.floorMod(++mRandom, size - (i + 1));
- if (valueArray.get(i + 1 + rnd).get() == null) {
- remove(myHash, i + 1 + rnd);
- }
- }
- return;
- }
- }
- valueArray.add(size, newWr);
- Long[] keyArray = mMainIndexKeys[myHash];
- if (keyArray.length == size) {
- // size >= 1, since we initially allocated one element
- Long[] newArray = new Long[size + size / 2 + 2];
- System.arraycopy(keyArray, 0, newArray, 0, size);
- newArray[size] = key;
- mMainIndexKeys[myHash] = newArray;
- } else {
- keyArray[size] = key;
- }
- if (size >= mWarnBucketSize) {
- final int totalSize = size();
- Log.v(Binder.TAG, "BinderProxy map growth! bucket size = " + size
- + " total = " + totalSize);
- mWarnBucketSize += WARN_INCREMENT;
- if (Build.IS_DEBUGGABLE && totalSize >= CRASH_AT_SIZE) {
- // Use the number of uncleared entries to determine whether we should
- // really report a histogram and crash. We don't want to fundamentally
- // change behavior for a debuggable process, so we GC only if we are
- // about to crash.
- final int totalUnclearedSize = unclearedSize();
- if (totalUnclearedSize >= CRASH_AT_SIZE) {
- dumpProxyInterfaceCounts();
- dumpPerUidProxyCounts();
- Runtime.getRuntime().gc();
- throw new AssertionError("Binder ProxyMap has too many entries: "
- + totalSize + " (total), " + totalUnclearedSize + " (uncleared), "
- + unclearedSize() + " (uncleared after GC). BinderProxy leak?");
- } else if (totalSize > 3 * totalUnclearedSize / 2) {
- Log.v(Binder.TAG, "BinderProxy map has many cleared entries: "
- + (totalSize - totalUnclearedSize) + " of " + totalSize
- + " are cleared");
- }
- }
- }
- }
-
- /**
- * Dump a histogram to the logcat. Used to diagnose abnormally large proxy maps.
- */
- private void dumpProxyInterfaceCounts() {
- Map<String, Integer> counts = new HashMap<>();
- for (ArrayList<WeakReference<BinderProxy>> a : mMainIndexValues) {
- if (a != null) {
- for (WeakReference<BinderProxy> weakRef : a) {
- BinderProxy bp = weakRef.get();
- String key;
- if (bp == null) {
- key = "<cleared weak-ref>";
- } else {
- try {
- key = bp.getInterfaceDescriptor();
- } catch (Throwable t) {
- key = "<exception during getDescriptor>";
- }
- }
- Integer i = counts.get(key);
- if (i == null) {
- counts.put(key, 1);
- } else {
- counts.put(key, i + 1);
- }
- }
- }
- }
- Map.Entry<String, Integer>[] sorted = counts.entrySet().toArray(
- new Map.Entry[counts.size()]);
- Arrays.sort(sorted, (Map.Entry<String, Integer> a, Map.Entry<String, Integer> b)
- -> b.getValue().compareTo(a.getValue()));
- Log.v(Binder.TAG, "BinderProxy descriptor histogram (top ten):");
- int printLength = Math.min(10, sorted.length);
- for (int i = 0; i < printLength; i++) {
- Log.v(Binder.TAG, " #" + (i + 1) + ": " + sorted[i].getKey() + " x"
- + sorted[i].getValue());
- }
- }
-
- /**
- * Dump per uid binder proxy counts to the logcat.
- */
- private void dumpPerUidProxyCounts() {
- SparseIntArray counts = BinderInternal.nGetBinderProxyPerUidCounts();
- if (counts.size() == 0) return;
- Log.d(Binder.TAG, "Per Uid Binder Proxy Counts:");
- for (int i = 0; i < counts.size(); i++) {
- final int uid = counts.keyAt(i);
- final int binderCount = counts.valueAt(i);
- Log.d(Binder.TAG, "UID : " + uid + " count = " + binderCount);
- }
- }
-
- // Corresponding ArrayLists in the following two arrays always have the same size.
- // They contain no empty entries. However WeakReferences in the values ArrayLists
- // may have been cleared.
-
- // mMainIndexKeys[i][j] corresponds to mMainIndexValues[i].get(j) .
- // The values ArrayList has the proper size(), the corresponding keys array
- // is always at least the same size, but may be larger.
- // If either a particular keys array, or the corresponding values ArrayList
- // are null, then they both are.
- private final Long[][] mMainIndexKeys = new Long[MAIN_INDEX_SIZE][];
- private final ArrayList<WeakReference<BinderProxy>>[] mMainIndexValues =
- new ArrayList[MAIN_INDEX_SIZE];
- }
-
- @GuardedBy("sProxyMap")
- private static final ProxyMap sProxyMap = new ProxyMap();
-
- /**
- * Dump proxy debug information.
- *
- * @hide
- */
- private static void dumpProxyDebugInfo() {
- if (Build.IS_DEBUGGABLE) {
- synchronized (sProxyMap) {
- sProxyMap.dumpProxyInterfaceCounts();
- }
- // Note that we don't call dumpPerUidProxyCounts(); this is because this
- // method may be called as part of the uid limit being hit, and calling
- // back into the UID tracking code would cause us to try to acquire a mutex
- // that is held during that callback.
- }
- }
-
- /**
- * Return a BinderProxy for IBinder.
- * If we previously returned a BinderProxy bp for the same iBinder, and bp is still
- * in use, then we return the same bp.
- *
- * @param nativeData C++ pointer to (possibly still empty) BinderProxyNativeData.
- * Takes ownership of nativeData iff <result>.mNativeData == nativeData, or if
- * we exit via an exception. If neither applies, it's the callers responsibility to
- * recycle nativeData.
- * @param iBinder C++ pointer to IBinder. Does not take ownership of referenced object.
- */
- private static BinderProxy getInstance(long nativeData, long iBinder) {
- BinderProxy result;
- synchronized (sProxyMap) {
- try {
- result = sProxyMap.get(iBinder);
- if (result != null) {
- return result;
- }
- result = new BinderProxy(nativeData);
- } catch (Throwable e) {
- // We're throwing an exception (probably OOME); don't drop nativeData.
- NativeAllocationRegistry.applyFreeFunction(NoImagePreloadHolder.sNativeFinalizer,
- nativeData);
- throw e;
- }
- NoImagePreloadHolder.sRegistry.registerNativeAllocation(result, nativeData);
- // The registry now owns nativeData, even if registration threw an exception.
- sProxyMap.set(iBinder, result);
- }
- return result;
- }
-
- private BinderProxy(long nativeData) {
- mNativeData = nativeData;
- }
-
- /**
- * Guestimate of native memory associated with a BinderProxy.
- * This includes the underlying IBinder, associated DeathRecipientList, and KeyedVector
- * that points back to us. We guess high since it includes a GlobalRef, which
- * may be in short supply.
- */
- private static final int NATIVE_ALLOCATION_SIZE = 1000;
-
- // Use a Holder to allow static initialization of BinderProxy in the boot image, and
- // to avoid some initialization ordering issues.
- private static class NoImagePreloadHolder {
- public static final long sNativeFinalizer = getNativeFinalizer();
- public static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
- BinderProxy.class.getClassLoader(), sNativeFinalizer, NATIVE_ALLOCATION_SIZE);
- }
-
- public native boolean pingBinder();
- public native boolean isBinderAlive();
-
- public IInterface queryLocalInterface(String descriptor) {
- return null;
- }
-
- public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
- Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");
-
- if (mWarnOnBlocking && ((flags & FLAG_ONEWAY) == 0)) {
- // For now, avoid spamming the log by disabling after we've logged
- // about this interface at least once
- mWarnOnBlocking = false;
- Log.w(Binder.TAG, "Outgoing transactions from this process must be FLAG_ONEWAY",
- new Throwable());
- }
-
- final boolean tracingEnabled = Binder.isTracingEnabled();
- if (tracingEnabled) {
- final Throwable tr = new Throwable();
- Binder.getTransactionTracker().addTrace(tr);
- StackTraceElement stackTraceElement = tr.getStackTrace()[1];
- Trace.traceBegin(Trace.TRACE_TAG_ALWAYS,
- stackTraceElement.getClassName() + "." + stackTraceElement.getMethodName());
- }
- try {
- return transactNative(code, data, reply, flags);
- } finally {
- if (tracingEnabled) {
- Trace.traceEnd(Trace.TRACE_TAG_ALWAYS);
- }
- }
- }
-
- private static native long getNativeFinalizer();
- public native String getInterfaceDescriptor() throws RemoteException;
- public native boolean transactNative(int code, Parcel data, Parcel reply,
- int flags) throws RemoteException;
- public native void linkToDeath(DeathRecipient recipient, int flags)
- throws RemoteException;
- public native boolean unlinkToDeath(DeathRecipient recipient, int flags);
-
- public void dump(FileDescriptor fd, String[] args) throws RemoteException {
- Parcel data = Parcel.obtain();
- Parcel reply = Parcel.obtain();
- data.writeFileDescriptor(fd);
- data.writeStringArray(args);
- try {
- transact(DUMP_TRANSACTION, data, reply, 0);
- reply.readException();
- } finally {
- data.recycle();
- reply.recycle();
- }
- }
-
- public void dumpAsync(FileDescriptor fd, String[] args) throws RemoteException {
- Parcel data = Parcel.obtain();
- Parcel reply = Parcel.obtain();
- data.writeFileDescriptor(fd);
- data.writeStringArray(args);
- try {
- transact(DUMP_TRANSACTION, data, reply, FLAG_ONEWAY);
- } finally {
- data.recycle();
- reply.recycle();
- }
- }
-
- public void shellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
- String[] args, ShellCallback callback,
- ResultReceiver resultReceiver) throws RemoteException {
- Parcel data = Parcel.obtain();
- Parcel reply = Parcel.obtain();
- data.writeFileDescriptor(in);
- data.writeFileDescriptor(out);
- data.writeFileDescriptor(err);
- data.writeStringArray(args);
- ShellCallback.writeToParcel(callback, data);
- resultReceiver.writeToParcel(data, 0);
- try {
- transact(SHELL_COMMAND_TRANSACTION, data, reply, 0);
- reply.readException();
- } finally {
- data.recycle();
- reply.recycle();
- }
- }
-
- private static final void sendDeathNotice(DeathRecipient recipient) {
- if (false) Log.v("JavaBinder", "sendDeathNotice to " + recipient);
- try {
- recipient.binderDied();
- }
- catch (RuntimeException exc) {
- Log.w("BinderNative", "Uncaught exception from death notification",
- exc);
- }
- }
-
- /**
- * C++ pointer to BinderProxyNativeData. That consists of strong pointers to the
- * native IBinder object, and a DeathRecipientList.
- */
- private final long mNativeData;
-}
diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java
new file mode 100644
index 0000000..5752b6f
--- /dev/null
+++ b/core/java/android/os/BinderProxy.java
@@ -0,0 +1,588 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.annotation.NonNull;
+import android.util.Log;
+import android.util.SparseIntArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.BinderInternal;
+
+import libcore.util.NativeAllocationRegistry;
+
+import java.io.FileDescriptor;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Java proxy for a native IBinder object.
+ * Allocated and constructed by the native javaObjectforIBinder function. Never allocated
+ * directly from Java code.
+ *
+ * @hide
+ */
+public final class BinderProxy implements IBinder {
+ // See android_util_Binder.cpp for the native half of this.
+
+ // Assume the process-wide default value when created
+ volatile boolean mWarnOnBlocking = Binder.sWarnOnBlocking;
+
+ /*
+ * Map from longs to BinderProxy, retaining only a WeakReference to the BinderProxies.
+ * We roll our own only because we need to lazily remove WeakReferences during accesses
+ * to avoid accumulating junk WeakReference objects. WeakHashMap isn't easily usable
+ * because we want weak values, not keys.
+ * Our hash table is never resized, but the number of entries is unlimited;
+ * performance degrades as occupancy increases significantly past MAIN_INDEX_SIZE.
+ * Not thread-safe. Client ensures there's a single access at a time.
+ */
+ private static final class ProxyMap {
+ private static final int LOG_MAIN_INDEX_SIZE = 8;
+ private static final int MAIN_INDEX_SIZE = 1 << LOG_MAIN_INDEX_SIZE;
+ private static final int MAIN_INDEX_MASK = MAIN_INDEX_SIZE - 1;
+ // Debuggable builds will throw an AssertionError if the number of map entries exceeds:
+ private static final int CRASH_AT_SIZE = 20_000;
+
+ /**
+ * We next warn when we exceed this bucket size.
+ */
+ private int mWarnBucketSize = 20;
+
+ /**
+ * Increment mWarnBucketSize by WARN_INCREMENT each time we warn.
+ */
+ private static final int WARN_INCREMENT = 10;
+
+ /**
+ * Hash function tailored to native pointers.
+ * Returns a value < MAIN_INDEX_SIZE.
+ */
+ private static int hash(long arg) {
+ return ((int) ((arg >> 2) ^ (arg >> (2 + LOG_MAIN_INDEX_SIZE)))) & MAIN_INDEX_MASK;
+ }
+
+ /**
+ * Return the total number of pairs in the map.
+ */
+ private int size() {
+ int size = 0;
+ for (ArrayList<WeakReference<BinderProxy>> a : mMainIndexValues) {
+ if (a != null) {
+ size += a.size();
+ }
+ }
+ return size;
+ }
+
+ /**
+ * Return the total number of pairs in the map containing values that have
+ * not been cleared. More expensive than the above size function.
+ */
+ private int unclearedSize() {
+ int size = 0;
+ for (ArrayList<WeakReference<BinderProxy>> a : mMainIndexValues) {
+ if (a != null) {
+ for (WeakReference<BinderProxy> ref : a) {
+ if (ref.get() != null) {
+ ++size;
+ }
+ }
+ }
+ }
+ return size;
+ }
+
+ /**
+ * Remove ith entry from the hash bucket indicated by hash.
+ */
+ private void remove(int hash, int index) {
+ Long[] keyArray = mMainIndexKeys[hash];
+ ArrayList<WeakReference<BinderProxy>> valueArray = mMainIndexValues[hash];
+ int size = valueArray.size(); // KeyArray may have extra elements.
+ // Move last entry into empty slot, and truncate at end.
+ if (index != size - 1) {
+ keyArray[index] = keyArray[size - 1];
+ valueArray.set(index, valueArray.get(size - 1));
+ }
+ valueArray.remove(size - 1);
+ // Just leave key array entry; it's unused. We only trust the valueArray size.
+ }
+
+ /**
+ * Look up the supplied key. If we have a non-cleared entry for it, return it.
+ */
+ BinderProxy get(long key) {
+ int myHash = hash(key);
+ Long[] keyArray = mMainIndexKeys[myHash];
+ if (keyArray == null) {
+ return null;
+ }
+ ArrayList<WeakReference<BinderProxy>> valueArray = mMainIndexValues[myHash];
+ int bucketSize = valueArray.size();
+ for (int i = 0; i < bucketSize; ++i) {
+ long foundKey = keyArray[i];
+ if (key == foundKey) {
+ WeakReference<BinderProxy> wr = valueArray.get(i);
+ BinderProxy bp = wr.get();
+ if (bp != null) {
+ return bp;
+ } else {
+ remove(myHash, i);
+ return null;
+ }
+ }
+ }
+ return null;
+ }
+
+ private int mRandom; // A counter used to generate a "random" index. World's 2nd worst RNG.
+
+ /**
+ * Add the key-value pair to the map.
+ * Requires that the indicated key is not already in the map.
+ */
+ void set(long key, @NonNull BinderProxy value) {
+ int myHash = hash(key);
+ ArrayList<WeakReference<BinderProxy>> valueArray = mMainIndexValues[myHash];
+ if (valueArray == null) {
+ valueArray = mMainIndexValues[myHash] = new ArrayList<>();
+ mMainIndexKeys[myHash] = new Long[1];
+ }
+ int size = valueArray.size();
+ WeakReference<BinderProxy> newWr = new WeakReference<>(value);
+ // First look for a cleared reference.
+ // This ensures that ArrayList size is bounded by the maximum occupancy of
+ // that bucket.
+ for (int i = 0; i < size; ++i) {
+ if (valueArray.get(i).get() == null) {
+ valueArray.set(i, newWr);
+ Long[] keyArray = mMainIndexKeys[myHash];
+ keyArray[i] = key;
+ if (i < size - 1) {
+ // "Randomly" check one of the remaining entries in [i+1, size), so that
+ // needlessly long buckets are eventually pruned.
+ int rnd = Math.floorMod(++mRandom, size - (i + 1));
+ if (valueArray.get(i + 1 + rnd).get() == null) {
+ remove(myHash, i + 1 + rnd);
+ }
+ }
+ return;
+ }
+ }
+ valueArray.add(size, newWr);
+ Long[] keyArray = mMainIndexKeys[myHash];
+ if (keyArray.length == size) {
+ // size >= 1, since we initially allocated one element
+ Long[] newArray = new Long[size + size / 2 + 2];
+ System.arraycopy(keyArray, 0, newArray, 0, size);
+ newArray[size] = key;
+ mMainIndexKeys[myHash] = newArray;
+ } else {
+ keyArray[size] = key;
+ }
+ if (size >= mWarnBucketSize) {
+ final int totalSize = size();
+ Log.v(Binder.TAG, "BinderProxy map growth! bucket size = " + size
+ + " total = " + totalSize);
+ mWarnBucketSize += WARN_INCREMENT;
+ if (Build.IS_DEBUGGABLE && totalSize >= CRASH_AT_SIZE) {
+ // Use the number of uncleared entries to determine whether we should
+ // really report a histogram and crash. We don't want to fundamentally
+ // change behavior for a debuggable process, so we GC only if we are
+ // about to crash.
+ final int totalUnclearedSize = unclearedSize();
+ if (totalUnclearedSize >= CRASH_AT_SIZE) {
+ dumpProxyInterfaceCounts();
+ dumpPerUidProxyCounts();
+ Runtime.getRuntime().gc();
+ throw new AssertionError("Binder ProxyMap has too many entries: "
+ + totalSize + " (total), " + totalUnclearedSize + " (uncleared), "
+ + unclearedSize() + " (uncleared after GC). BinderProxy leak?");
+ } else if (totalSize > 3 * totalUnclearedSize / 2) {
+ Log.v(Binder.TAG, "BinderProxy map has many cleared entries: "
+ + (totalSize - totalUnclearedSize) + " of " + totalSize
+ + " are cleared");
+ }
+ }
+ }
+ }
+
+ private InterfaceCount[] getSortedInterfaceCounts(int maxToReturn) {
+ if (maxToReturn < 0) {
+ throw new IllegalArgumentException("negative interface count");
+ }
+
+ Map<String, Integer> counts = new HashMap<>();
+ for (ArrayList<WeakReference<BinderProxy>> a : mMainIndexValues) {
+ if (a != null) {
+ for (WeakReference<BinderProxy> weakRef : a) {
+ BinderProxy bp = weakRef.get();
+ String key;
+ if (bp == null) {
+ key = "<cleared weak-ref>";
+ } else {
+ try {
+ key = bp.getInterfaceDescriptor();
+ } catch (Throwable t) {
+ key = "<exception during getDescriptor>";
+ }
+ }
+ Integer i = counts.get(key);
+ if (i == null) {
+ counts.put(key, 1);
+ } else {
+ counts.put(key, i + 1);
+ }
+ }
+ }
+ }
+ Map.Entry<String, Integer>[] sorted = counts.entrySet().toArray(
+ new Map.Entry[counts.size()]);
+
+ Arrays.sort(sorted, (Map.Entry<String, Integer> a, Map.Entry<String, Integer> b)
+ -> b.getValue().compareTo(a.getValue()));
+
+ int returnCount = Math.min(maxToReturn, sorted.length);
+ InterfaceCount[] ifaceCounts = new InterfaceCount[returnCount];
+ for (int i = 0; i < returnCount; i++) {
+ ifaceCounts[i] = new InterfaceCount(sorted[i].getKey(), sorted[i].getValue());
+ }
+ return ifaceCounts;
+ }
+
+ static final int MAX_NUM_INTERFACES_TO_DUMP = 10;
+
+ /**
+ * Dump a histogram to the logcat. Used to diagnose abnormally large proxy maps.
+ */
+ private void dumpProxyInterfaceCounts() {
+ final InterfaceCount[] sorted = getSortedInterfaceCounts(MAX_NUM_INTERFACES_TO_DUMP);
+
+ Log.v(Binder.TAG, "BinderProxy descriptor histogram "
+ + "(top " + Integer.toString(MAX_NUM_INTERFACES_TO_DUMP) + "):");
+ for (int i = 0; i < sorted.length; i++) {
+ Log.v(Binder.TAG, " #" + (i + 1) + ": " + sorted[i]);
+ }
+ }
+
+ /**
+ * Dump per uid binder proxy counts to the logcat.
+ */
+ private void dumpPerUidProxyCounts() {
+ SparseIntArray counts = BinderInternal.nGetBinderProxyPerUidCounts();
+ if (counts.size() == 0) return;
+ Log.d(Binder.TAG, "Per Uid Binder Proxy Counts:");
+ for (int i = 0; i < counts.size(); i++) {
+ final int uid = counts.keyAt(i);
+ final int binderCount = counts.valueAt(i);
+ Log.d(Binder.TAG, "UID : " + uid + " count = " + binderCount);
+ }
+ }
+
+ // Corresponding ArrayLists in the following two arrays always have the same size.
+ // They contain no empty entries. However WeakReferences in the values ArrayLists
+ // may have been cleared.
+
+ // mMainIndexKeys[i][j] corresponds to mMainIndexValues[i].get(j) .
+ // The values ArrayList has the proper size(), the corresponding keys array
+ // is always at least the same size, but may be larger.
+ // If either a particular keys array, or the corresponding values ArrayList
+ // are null, then they both are.
+ private final Long[][] mMainIndexKeys = new Long[MAIN_INDEX_SIZE][];
+ private final ArrayList<WeakReference<BinderProxy>>[] mMainIndexValues =
+ new ArrayList[MAIN_INDEX_SIZE];
+ }
+
+ @GuardedBy("sProxyMap")
+ private static final ProxyMap sProxyMap = new ProxyMap();
+
+ /**
+ * Simple pair-value class to store number of binder proxy interfaces live in this process.
+ */
+ public static final class InterfaceCount {
+ private final String mInterfaceName;
+ private final int mCount;
+
+ InterfaceCount(String interfaceName, int count) {
+ mInterfaceName = interfaceName;
+ mCount = count;
+ }
+
+ @Override
+ public String toString() {
+ return mInterfaceName + " x" + Integer.toString(mCount);
+ }
+ }
+
+ /**
+ * Get a sorted array with entries mapping proxy interface names to the number
+ * of live proxies with those names.
+ *
+ * @param num maximum number of proxy interface counts to return. Use
+ * Integer.MAX_VALUE to retrieve all
+ * @hide
+ */
+ public static InterfaceCount[] getSortedInterfaceCounts(int num) {
+ synchronized (sProxyMap) {
+ return sProxyMap.getSortedInterfaceCounts(num);
+ }
+ }
+
+ /**
+ * Dump proxy debug information.
+ *
+ * @hide
+ */
+ public static void dumpProxyDebugInfo() {
+ if (Build.IS_DEBUGGABLE) {
+ synchronized (sProxyMap) {
+ sProxyMap.dumpProxyInterfaceCounts();
+ sProxyMap.dumpPerUidProxyCounts();
+ }
+ }
+ }
+
+ /**
+ * Return a BinderProxy for IBinder.
+ * If we previously returned a BinderProxy bp for the same iBinder, and bp is still
+ * in use, then we return the same bp.
+ *
+ * @param nativeData C++ pointer to (possibly still empty) BinderProxyNativeData.
+ * Takes ownership of nativeData iff <result>.mNativeData == nativeData, or if
+ * we exit via an exception. If neither applies, it's the callers responsibility to
+ * recycle nativeData.
+ * @param iBinder C++ pointer to IBinder. Does not take ownership of referenced object.
+ */
+ private static BinderProxy getInstance(long nativeData, long iBinder) {
+ BinderProxy result;
+ synchronized (sProxyMap) {
+ try {
+ result = sProxyMap.get(iBinder);
+ if (result != null) {
+ return result;
+ }
+ result = new BinderProxy(nativeData);
+ } catch (Throwable e) {
+ // We're throwing an exception (probably OOME); don't drop nativeData.
+ NativeAllocationRegistry.applyFreeFunction(NoImagePreloadHolder.sNativeFinalizer,
+ nativeData);
+ throw e;
+ }
+ NoImagePreloadHolder.sRegistry.registerNativeAllocation(result, nativeData);
+ // The registry now owns nativeData, even if registration threw an exception.
+ sProxyMap.set(iBinder, result);
+ }
+ return result;
+ }
+
+ private BinderProxy(long nativeData) {
+ mNativeData = nativeData;
+ }
+
+ /**
+ * Guestimate of native memory associated with a BinderProxy.
+ * This includes the underlying IBinder, associated DeathRecipientList, and KeyedVector
+ * that points back to us. We guess high since it includes a GlobalRef, which
+ * may be in short supply.
+ */
+ private static final int NATIVE_ALLOCATION_SIZE = 1000;
+
+ // Use a Holder to allow static initialization of BinderProxy in the boot image, and
+ // to avoid some initialization ordering issues.
+ private static class NoImagePreloadHolder {
+ public static final long sNativeFinalizer = getNativeFinalizer();
+ public static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
+ BinderProxy.class.getClassLoader(), sNativeFinalizer, NATIVE_ALLOCATION_SIZE);
+ }
+
+ /**
+ * @return false if the hosting process is gone, otherwise whatever the remote returns
+ */
+ public native boolean pingBinder();
+
+ /**
+ * @return false if the hosting process is gone
+ */
+ public native boolean isBinderAlive();
+
+ /**
+ * Retrieve a local interface - always null in case of a proxy
+ */
+ public IInterface queryLocalInterface(String descriptor) {
+ return null;
+ }
+
+ /**
+ * Perform a binder transaction on a proxy.
+ *
+ * @param code The action to perform. This should
+ * be a number between {@link #FIRST_CALL_TRANSACTION} and
+ * {@link #LAST_CALL_TRANSACTION}.
+ * @param data Marshalled data to send to the target. Must not be null.
+ * If you are not sending any data, you must create an empty Parcel
+ * that is given here.
+ * @param reply Marshalled data to be received from the target. May be
+ * null if you are not interested in the return value.
+ * @param flags Additional operation flags. Either 0 for a normal
+ * RPC, or {@link #FLAG_ONEWAY} for a one-way RPC.
+ *
+ * @return
+ * @throws RemoteException
+ */
+ public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
+ Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");
+
+ if (mWarnOnBlocking && ((flags & FLAG_ONEWAY) == 0)) {
+ // For now, avoid spamming the log by disabling after we've logged
+ // about this interface at least once
+ mWarnOnBlocking = false;
+ Log.w(Binder.TAG, "Outgoing transactions from this process must be FLAG_ONEWAY",
+ new Throwable());
+ }
+
+ final boolean tracingEnabled = Binder.isTracingEnabled();
+ if (tracingEnabled) {
+ final Throwable tr = new Throwable();
+ Binder.getTransactionTracker().addTrace(tr);
+ StackTraceElement stackTraceElement = tr.getStackTrace()[1];
+ Trace.traceBegin(Trace.TRACE_TAG_ALWAYS,
+ stackTraceElement.getClassName() + "." + stackTraceElement.getMethodName());
+ }
+ try {
+ return transactNative(code, data, reply, flags);
+ } finally {
+ if (tracingEnabled) {
+ Trace.traceEnd(Trace.TRACE_TAG_ALWAYS);
+ }
+ }
+ }
+
+ /* Returns the native free function */
+ private static native long getNativeFinalizer();
+ /**
+ * See {@link IBinder#getInterfaceDescriptor()}
+ */
+ public native String getInterfaceDescriptor() throws RemoteException;
+
+ /**
+ * Native implementation of transact() for proxies
+ */
+ public native boolean transactNative(int code, Parcel data, Parcel reply,
+ int flags) throws RemoteException;
+ /**
+ * See {@link IBinder#linkToDeath(DeathRecipient, int)}
+ */
+ public native void linkToDeath(DeathRecipient recipient, int flags)
+ throws RemoteException;
+ /**
+ * See {@link IBinder#unlinkToDeath}
+ */
+ public native boolean unlinkToDeath(DeathRecipient recipient, int flags);
+
+ /**
+ * Perform a dump on the remote object
+ *
+ * @param fd The raw file descriptor that the dump is being sent to.
+ * @param args additional arguments to the dump request.
+ * @throws RemoteException
+ */
+ public void dump(FileDescriptor fd, String[] args) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeFileDescriptor(fd);
+ data.writeStringArray(args);
+ try {
+ transact(DUMP_TRANSACTION, data, reply, 0);
+ reply.readException();
+ } finally {
+ data.recycle();
+ reply.recycle();
+ }
+ }
+
+ /**
+ * Perform an asynchronous dump on the remote object
+ *
+ * @param fd The raw file descriptor that the dump is being sent to.
+ * @param args additional arguments to the dump request.
+ * @throws RemoteException
+ */
+ public void dumpAsync(FileDescriptor fd, String[] args) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeFileDescriptor(fd);
+ data.writeStringArray(args);
+ try {
+ transact(DUMP_TRANSACTION, data, reply, FLAG_ONEWAY);
+ } finally {
+ data.recycle();
+ reply.recycle();
+ }
+ }
+
+ /**
+ * See {@link IBinder#shellCommand(FileDescriptor, FileDescriptor, FileDescriptor,
+ * String[], ShellCallback, ResultReceiver)}
+ *
+ * @param in The raw file descriptor that an input data stream can be read from.
+ * @param out The raw file descriptor that normal command messages should be written to.
+ * @param err The raw file descriptor that command error messages should be written to.
+ * @param args Command-line arguments.
+ * @param callback Optional callback to the caller's shell to perform operations in it.
+ * @param resultReceiver Called when the command has finished executing, with the result code.
+ * @throws RemoteException
+ */
+ public void shellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
+ String[] args, ShellCallback callback,
+ ResultReceiver resultReceiver) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeFileDescriptor(in);
+ data.writeFileDescriptor(out);
+ data.writeFileDescriptor(err);
+ data.writeStringArray(args);
+ ShellCallback.writeToParcel(callback, data);
+ resultReceiver.writeToParcel(data, 0);
+ try {
+ transact(SHELL_COMMAND_TRANSACTION, data, reply, 0);
+ reply.readException();
+ } finally {
+ data.recycle();
+ reply.recycle();
+ }
+ }
+
+ private static void sendDeathNotice(DeathRecipient recipient) {
+ if (false) Log.v("JavaBinder", "sendDeathNotice to " + recipient);
+ try {
+ recipient.binderDied();
+ } catch (RuntimeException exc) {
+ Log.w("BinderNative", "Uncaught exception from death notification",
+ exc);
+ }
+ }
+
+ /**
+ * C++ pointer to BinderProxyNativeData. That consists of strong pointers to the
+ * native IBinder object, and a DeathRecipientList.
+ */
+ private final long mNativeData;
+}
diff --git a/core/java/android/text/PrecomputedText.java b/core/java/android/text/PrecomputedText.java
index 369f357..027ead3 100644
--- a/core/java/android/text/PrecomputedText.java
+++ b/core/java/android/text/PrecomputedText.java
@@ -437,7 +437,6 @@
public boolean canUseMeasuredResult(@IntRange(from = 0) int start, @IntRange(from = 0) int end,
@NonNull TextDirectionHeuristic textDir, @NonNull TextPaint paint,
@Layout.BreakStrategy int strategy, @Layout.HyphenationFrequency int frequency) {
- final TextPaint mtPaint = mParams.getTextPaint();
return mStart == start
&& mEnd == end
&& mParams.isSameTextMetricsInternal(paint, textDir, strategy, frequency);
diff --git a/core/java/android/widget/DateTimeView.java b/core/java/android/widget/DateTimeView.java
index 621a745..2b1e900 100644
--- a/core/java/android/widget/DateTimeView.java
+++ b/core/java/android/widget/DateTimeView.java
@@ -30,7 +30,6 @@
import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.database.ContentObserver;
-import android.icu.util.Calendar;
import android.os.Handler;
import android.text.format.Time;
import android.util.AttributeSet;
@@ -41,6 +40,7 @@
import java.text.DateFormat;
import java.util.ArrayList;
+import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
@@ -301,7 +301,7 @@
*/
private long computeNextMidnight(TimeZone timeZone) {
Calendar c = Calendar.getInstance();
- c.setTimeZone(libcore.icu.DateUtilsBridge.icuTimeZone(timeZone));
+ c.setTimeZone(timeZone);
c.add(Calendar.DAY_OF_MONTH, 1);
c.set(Calendar.HOUR_OF_DAY, 0);
c.set(Calendar.MINUTE, 0);
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 2e7ef8b..ea54696 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -6973,7 +6973,8 @@
public boolean hasOverlappingRendering() {
// horizontal fading edge causes SaveLayerAlpha, which doesn't support alpha modulation
return ((getBackground() != null && getBackground().getCurrent() != null)
- || mSpannable != null || hasSelection() || isHorizontalFadingEdgeEnabled());
+ || mSpannable != null || hasSelection() || isHorizontalFadingEdgeEnabled()
+ || mShadowColor != 0);
}
/**
diff --git a/core/java/com/android/internal/app/LocaleStore.java b/core/java/com/android/internal/app/LocaleStore.java
index d247013..1d997f5 100644
--- a/core/java/com/android/internal/app/LocaleStore.java
+++ b/core/java/com/android/internal/app/LocaleStore.java
@@ -21,6 +21,7 @@
import android.provider.Settings;
import android.telephony.TelephonyManager;
+import java.io.Serializable;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IllformedLocaleException;
@@ -31,7 +32,7 @@
private static final HashMap<String, LocaleInfo> sLocaleCache = new HashMap<>();
private static boolean sFullyInitialized = false;
- public static class LocaleInfo {
+ public static class LocaleInfo implements Serializable {
private static final int SUGGESTION_TYPE_NONE = 0;
private static final int SUGGESTION_TYPE_SIM = 1 << 0;
private static final int SUGGESTION_TYPE_CFG = 1 << 1;
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 7e2bad2..ecad6c0 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -108,7 +108,6 @@
jclass mClass;
jmethodID mGetInstance;
jmethodID mSendDeathNotice;
- jmethodID mDumpProxyDebugInfo;
// Object state.
jfieldID mNativeData; // Field holds native pointer to BinderProxyNativeData.
@@ -994,16 +993,6 @@
static void android_os_BinderInternal_proxyLimitcallback(int uid)
{
JNIEnv *env = AndroidRuntime::getJNIEnv();
- {
- env->CallStaticObjectMethod(gBinderProxyOffsets.mClass,
- gBinderProxyOffsets.mDumpProxyDebugInfo);
- }
- if (env->ExceptionCheck()) {
- ScopedLocalRef<jthrowable> excep(env, env->ExceptionOccurred());
- report_exception(env, excep.get(),
- "*** Uncaught exception in dumpProxyDebugInfo");
- }
-
env->CallStaticVoidMethod(gBinderInternalOffsets.mClass,
gBinderInternalOffsets.mProxyLimitCallback,
uid);
@@ -1390,8 +1379,6 @@
"(JJ)Landroid/os/BinderProxy;");
gBinderProxyOffsets.mSendDeathNotice = GetStaticMethodIDOrDie(env, clazz, "sendDeathNotice",
"(Landroid/os/IBinder$DeathRecipient;)V");
- gBinderProxyOffsets.mDumpProxyDebugInfo = GetStaticMethodIDOrDie(env, clazz, "dumpProxyDebugInfo",
- "()V");
gBinderProxyOffsets.mNativeData = GetFieldIDOrDie(env, clazz, "mNativeData", "J");
clazz = FindClassOrDie(env, "java/lang/Class");
diff --git a/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java b/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java
index 15dbbc8..c98e646 100644
--- a/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java
+++ b/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java
@@ -20,6 +20,7 @@
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.Suppress;
import android.text.InputType;
import android.util.KeyUtils;
import android.view.KeyEvent;
@@ -238,6 +239,7 @@
}
@Test
+ @Suppress
public void testEmojiModifier() {
EditorState state = new EditorState();
@@ -267,6 +269,7 @@
}
@Test
+ @Suppress
public void testMixedEdgeCases() {
EditorState state = new EditorState();
diff --git a/libs/androidfw/ChunkIterator.cpp b/libs/androidfw/ChunkIterator.cpp
index d8adbe5..8fc3219 100644
--- a/libs/androidfw/ChunkIterator.cpp
+++ b/libs/androidfw/ChunkIterator.cpp
@@ -32,11 +32,30 @@
if (len_ != 0) {
// Prepare the next chunk.
- VerifyNextChunk();
+ if (VerifyNextChunkNonFatal()) {
+ VerifyNextChunk();
+ }
}
return Chunk(this_chunk);
}
+// TODO(b/111401637) remove this and have full resource file verification
+// Returns false if there was an error.
+bool ChunkIterator::VerifyNextChunkNonFatal() {
+ if (len_ < sizeof(ResChunk_header)) {
+ last_error_ = "not enough space for header";
+ last_error_was_fatal_ = false;
+ return false;
+ }
+ const size_t size = dtohl(next_chunk_->size);
+ if (size > len_) {
+ last_error_ = "chunk size is bigger than given data";
+ last_error_was_fatal_ = false;
+ return false;
+ }
+ return true;
+}
+
// Returns false if there was an error.
bool ChunkIterator::VerifyNextChunk() {
const uintptr_t header_start = reinterpret_cast<uintptr_t>(next_chunk_);
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index 04d506a..c2740c9 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -560,7 +560,9 @@
if (iter.HadError()) {
LOG(ERROR) << iter.GetLastError();
- return {};
+ if (iter.HadFatalError()) {
+ return {};
+ }
}
// Flatten and construct the TypeSpecs.
@@ -641,7 +643,9 @@
if (iter.HadError()) {
LOG(ERROR) << iter.GetLastError();
- return false;
+ if (iter.HadFatalError()) {
+ return false;
+ }
}
return true;
}
@@ -673,7 +677,9 @@
if (iter.HadError()) {
LOG(ERROR) << iter.GetLastError();
- return {};
+ if (iter.HadFatalError()) {
+ return {};
+ }
}
// Need to force a move for mingw32.
diff --git a/libs/androidfw/include/androidfw/Chunk.h b/libs/androidfw/include/androidfw/Chunk.h
index 89b588e..99a52dc 100644
--- a/libs/androidfw/include/androidfw/Chunk.h
+++ b/libs/androidfw/include/androidfw/Chunk.h
@@ -94,18 +94,27 @@
Chunk Next();
inline bool HasNext() const { return !HadError() && len_ != 0; };
+ // Returns whether there was an error and processing should stop
inline bool HadError() const { return last_error_ != nullptr; }
inline std::string GetLastError() const { return last_error_; }
+ // Returns whether there was an error and processing should stop. For legacy purposes,
+ // some errors are considered "non fatal". Fatal errors stop processing new chunks and
+ // throw away any chunks already processed. Non fatal errors also stop processing new
+ // chunks, but, will retain and use any valid chunks already processed.
+ inline bool HadFatalError() const { return HadError() && last_error_was_fatal_; }
private:
DISALLOW_COPY_AND_ASSIGN(ChunkIterator);
// Returns false if there was an error.
bool VerifyNextChunk();
+ // Returns false if there was an error. For legacy purposes.
+ bool VerifyNextChunkNonFatal();
const ResChunk_header* next_chunk_;
size_t len_;
const char* last_error_;
+ bool last_error_was_fatal_ = true;
};
} // namespace android
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp
index 14d31b2..c41f6a6 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.cpp
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -82,7 +82,14 @@
textureMatrix = textureMatrixInv;
}
- SkMatrix matrix = SkMatrix::Concat(layerTransform, textureMatrix);
+ SkMatrix matrix;
+ if (dstRect) {
+ // Destination rectangle is set only when we are trying to read back the content
+ // of the layer. In this case we don't want to apply layer transform.
+ matrix = textureMatrix;
+ } else {
+ matrix = SkMatrix::Concat(layerTransform, textureMatrix);
+ }
SkPaint paint;
paint.setAlpha(layer->getAlpha());
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 10edf73..a19edae 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -327,8 +327,6 @@
}
return vkGetInstanceProcAddr(instance, proc_name);
};
- auto interface =
- sk_make_sp<GrVkInterface>(getProc, mInstance, mDevice, extensionFlags);
GrVkBackendContext backendContext;
backendContext.fInstance = mInstance;
@@ -339,7 +337,7 @@
backendContext.fMinAPIVersion = VK_MAKE_VERSION(1, 0, 0);
backendContext.fExtensions = extensionFlags;
backendContext.fFeatures = featureFlags;
- backendContext.fInterface = std::move(interface);
+ backendContext.fGetProc = std::move(getProc);
backendContext.fOwnsInstanceAndDevice = false;
// create the command pool for the command buffers
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 6eb3d8d..ee9e732 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -22,7 +22,6 @@
import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
import android.Manifest;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
@@ -42,16 +41,11 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.util.ArraySet;
import android.util.Log;
import com.android.internal.location.ProviderProperties;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
-import java.util.Set;
/**
* This class provides access to the system location services. These
@@ -331,7 +325,7 @@
Message msg = Message.obtain();
msg.what = TYPE_LOCATION_CHANGED;
msg.obj = location;
- mListenerHandler.sendMessage(msg);
+ sendCallbackMessage(msg);
}
@Override
@@ -345,7 +339,7 @@
b.putBundle("extras", extras);
}
msg.obj = b;
- mListenerHandler.sendMessage(msg);
+ sendCallbackMessage(msg);
}
@Override
@@ -353,7 +347,7 @@
Message msg = Message.obtain();
msg.what = TYPE_PROVIDER_ENABLED;
msg.obj = provider;
- mListenerHandler.sendMessage(msg);
+ sendCallbackMessage(msg);
}
@Override
@@ -361,7 +355,13 @@
Message msg = Message.obtain();
msg.what = TYPE_PROVIDER_DISABLED;
msg.obj = provider;
- mListenerHandler.sendMessage(msg);
+ sendCallbackMessage(msg);
+ }
+
+ private void sendCallbackMessage(Message msg) {
+ if (!mListenerHandler.sendMessage(msg)) {
+ locationCallbackFinished();
+ }
}
private void _handleMessage(Message msg) {
@@ -384,6 +384,10 @@
mListener.onProviderDisabled((String) msg.obj);
break;
}
+ locationCallbackFinished();
+ }
+
+ private void locationCallbackFinished() {
try {
mService.locationCallbackFinished(this);
} catch (RemoteException e) {
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index 7c68b55..2a2f4fe 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -143,7 +143,7 @@
* MIME type for HEIF still image data encoded in HEVC.
*
* To decode such an image, {@link MediaCodec} decoder for
- * {@ #MIMETYPE_VIDEO_HEVC} shall be used. The client needs to form
+ * {@link #MIMETYPE_VIDEO_HEVC} shall be used. The client needs to form
* the correct {@link #MediaFormat} based on additional information in
* the track format, and send it to {@link MediaCodec#configure}.
*
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index ada91be..d532e52 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -2510,10 +2510,10 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mTrackType);
+ dest.writeString(mFormat.getString(MediaFormat.KEY_MIME));
dest.writeString(getLanguage());
if (mTrackType == MEDIA_TRACK_TYPE_SUBTITLE) {
- dest.writeString(mFormat.getString(MediaFormat.KEY_MIME));
dest.writeInt(mFormat.getInteger(MediaFormat.KEY_IS_AUTOSELECT));
dest.writeInt(mFormat.getInteger(MediaFormat.KEY_IS_DEFAULT));
dest.writeInt(mFormat.getInteger(MediaFormat.KEY_IS_FORCED_SUBTITLE));
diff --git a/packages/SystemUI/res/layout/contextual.xml b/packages/SystemUI/res/layout/contextual.xml
index 94591e9..c8f0a24 100644
--- a/packages/SystemUI/res/layout/contextual.xml
+++ b/packages/SystemUI/res/layout/contextual.xml
@@ -20,6 +20,7 @@
android:layout_width="@dimen/navigation_key_width"
android:layout_height="match_parent"
android:importantForAccessibility="no"
+ android:focusable="false"
android:clipChildren="false"
android:clipToPadding="false"
>
diff --git a/packages/SystemUI/res/layout/menu_ime.xml b/packages/SystemUI/res/layout/menu_ime.xml
index 9130fb4..8a3a0b1 100644
--- a/packages/SystemUI/res/layout/menu_ime.xml
+++ b/packages/SystemUI/res/layout/menu_ime.xml
@@ -17,13 +17,13 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:systemui="http://schemas.android.com/apk/res-auto"
android:id="@+id/menu_container"
- android:layout_width="match_parent"
+ android:layout_width="@dimen/navigation_key_width"
android:layout_height="match_parent"
android:importantForAccessibility="no"
>
- <!-- Use width & height=match_parent for parent FrameLayout and buttons because they are placed
- inside a view that has a size controlled by weight. Ensure weight is large enough to support
- icon size. -->
+ <!-- Use nav button width & height=match_parent for parent FrameLayout and buttons because they
+ are placed inside a view that has a size controlled by weight. Ensure weight is large enough to
+ support icon size. -->
<com.android.systemui.statusbar.policy.KeyButtonView
android:id="@+id/menu"
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index cd1bd67..574288a 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -38,6 +38,7 @@
import android.app.IActivityManager;
import android.app.IBackupAgent;
import android.app.PendingIntent;
+import android.app.backup.BackupAgent;
import android.app.backup.BackupManager;
import android.app.backup.BackupManagerMonitor;
import android.app.backup.FullBackup;
@@ -704,7 +705,7 @@
* process-local non-lifecycle agent instance, so we manually set up the context
* topology for it.
*/
- public PackageManagerBackupAgent makeMetadataAgent() {
+ public BackupAgent makeMetadataAgent() {
PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(mPackageManager);
pmAgent.attach(mContext);
pmAgent.onCreate();
@@ -784,7 +785,7 @@
}
@VisibleForTesting
- BackupManagerService(
+ public BackupManagerService(
Context context,
Trampoline parent,
HandlerThread backupThread,
@@ -1749,6 +1750,16 @@
}
}
+ public void putOperation(int token, Operation operation) {
+ if (MORE_DEBUG) {
+ Slog.d(TAG, "Adding operation token=" + Integer.toHexString(token) + ", operation type="
+ + operation.type);
+ }
+ synchronized (mCurrentOpLock) {
+ mCurrentOperations.put(token, operation);
+ }
+ }
+
public void removeOperation(int token) {
if (MORE_DEBUG) {
Slog.d(TAG, "Removing operation token=" + Integer.toHexString(token));
diff --git a/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java b/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
index ae43299..90b4bbd 100644
--- a/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
+++ b/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
@@ -30,6 +30,7 @@
import android.annotation.Nullable;
import android.app.ApplicationThreadConstants;
import android.app.IBackupAgent;
+import android.app.backup.BackupAgent;
import android.app.backup.BackupDataInput;
import android.app.backup.BackupDataOutput;
import android.app.backup.BackupManager;
@@ -205,10 +206,8 @@
* Put this task in the repository of running tasks.
*/
private void registerTask() {
- synchronized (backupManagerService.getCurrentOpLock()) {
- backupManagerService.getCurrentOperations().put(
- mCurrentOpToken, new Operation(OP_PENDING, this, OP_TYPE_BACKUP));
- }
+ backupManagerService.putOperation(
+ mCurrentOpToken, new Operation(OP_PENDING, this, OP_TYPE_BACKUP));
}
/**
@@ -358,7 +357,7 @@
// The package manager doesn't have a proper <application> etc, but since it's running
// here in the system process we can just set up its agent directly and use a synthetic
// BackupRequest.
- PackageManagerBackupAgent pmAgent = backupManagerService.makeMetadataAgent();
+ BackupAgent pmAgent = backupManagerService.makeMetadataAgent();
mStatus = invokeAgentForBackup(
PACKAGE_MANAGER_SENTINEL,
IBackupAgent.Stub.asInterface(pmAgent.onBind()));
diff --git a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
index 78b000d..32dbad9 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
@@ -30,6 +30,7 @@
import android.app.IBackupAgent;
+import android.app.backup.BackupAgent;
import android.app.backup.IFullBackupRestoreObserver;
import android.content.pm.ApplicationInfo;
import android.content.pm.Signature;
@@ -76,7 +77,7 @@
private final String mCurrentPassword;
private final String mDecryptPassword;
private final AtomicBoolean mLatchObject;
- private final PackageManagerBackupAgent mPackageManagerBackupAgent;
+ private final BackupAgent mPackageManagerBackupAgent;
private final RestoreDeleteObserver mDeleteObserver = new RestoreDeleteObserver();
private IFullBackupRestoreObserver mObserver;
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 776cf47..232c151 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -795,6 +795,7 @@
* location updates.
*/
private final class Receiver implements IBinder.DeathRecipient, PendingIntent.OnFinished {
+ private static final long WAKELOCK_TIMEOUT_MILLIS = 60 * 1000;
final Identity mIdentity;
final int mAllowedResolutionLevel; // resolution level allowed to receiver
@@ -838,6 +839,10 @@
workSource = new WorkSource(mIdentity.mUid, mIdentity.mPackageName);
}
mWakeLock.setWorkSource(workSource);
+
+ // For a non-reference counted wakelock, each acquire will reset the timeout, and we
+ // only need to release it once.
+ mWakeLock.setReferenceCounted(false);
}
@Override
@@ -1099,9 +1104,8 @@
// this must be called while synchronized by caller in a synchronized block
// containing the sending of the broadcaset
private void incrementPendingBroadcastsLocked() {
- if (mPendingBroadcasts++ == 0) {
- mWakeLock.acquire();
- }
+ mPendingBroadcasts++;
+ mWakeLock.acquire(WAKELOCK_TIMEOUT_MILLIS);
}
private void decrementPendingBroadcastsLocked() {
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 59093c1..e7a8221 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -21,6 +21,7 @@
import android.os.Build;
import android.os.RemoteException;
import android.system.ErrnoException;
+import android.system.Os;
import android.system.OsConstants;
import android.system.StructRlimit;
import com.android.internal.os.ZygoteConnectionConstants;
@@ -47,6 +48,10 @@
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -621,23 +626,38 @@
mFdHighWaterMark = fdThreshold;
}
+ /**
+ * Dumps open file descriptors and their full paths to a temporary file in {@code mDumpDir}.
+ */
private void dumpOpenDescriptors() {
+ // We cannot exec lsof to get more info about open file descriptors because a newly
+ // forked process will not have the permissions to readlink. Instead list all open
+ // descriptors from /proc/pid/fd and resolve them.
+ List<String> dumpInfo = new ArrayList<>();
+ String fdDirPath = String.format("/proc/%d/fd/", Process.myPid());
+ File[] fds = new File(fdDirPath).listFiles();
+ if (fds == null) {
+ dumpInfo.add("Unable to list " + fdDirPath);
+ } else {
+ for (File f : fds) {
+ String fdSymLink = f.getAbsolutePath();
+ String resolvedPath = "";
+ try {
+ resolvedPath = Os.readlink(fdSymLink);
+ } catch (ErrnoException ex) {
+ resolvedPath = ex.getMessage();
+ }
+ dumpInfo.add(fdSymLink + "\t" + resolvedPath);
+ }
+ }
+
+ // Dump the fds & paths to a temp file.
try {
File dumpFile = File.createTempFile("anr_fd_", "", mDumpDir);
- java.lang.Process proc = new ProcessBuilder()
- .command("/system/bin/lsof", "-p", String.valueOf(Process.myPid()))
- .redirectErrorStream(true)
- .redirectOutput(dumpFile)
- .start();
-
- int returnCode = proc.waitFor();
- if (returnCode != 0) {
- Slog.w(TAG, "Unable to dump open descriptors, lsof return code: "
- + returnCode);
- dumpFile.delete();
- }
- } catch (IOException | InterruptedException ex) {
- Slog.w(TAG, "Unable to dump open descriptors: " + ex);
+ Path out = Paths.get(dumpFile.getAbsolutePath());
+ Files.write(out, dumpInfo, StandardCharsets.UTF_8);
+ } catch (IOException ex) {
+ Slog.w(TAG, "Unable to write open descriptors to file: " + ex);
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 48f4b22..f02e8f6 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -242,6 +242,7 @@
import android.net.Uri;
import android.os.BatteryStats;
import android.os.Binder;
+import android.os.BinderProxy;
import android.os.Build;
import android.os.Bundle;
import android.os.Debug;
@@ -11577,6 +11578,7 @@
public void onLimitReached(int uid) {
Slog.wtf(TAG, "Uid " + uid + " sent too many Binders to uid "
+ Process.myUid());
+ BinderProxy.dumpProxyDebugInfo();
if (uid == Process.SYSTEM_UID) {
Slog.i(TAG, "Skipping kill (uid is SYSTEM)");
} else {
@@ -12122,7 +12124,7 @@
return imp;
}
- private void fillInProcMemInfo(ProcessRecord app,
+ private void fillInProcMemInfoLocked(ProcessRecord app,
ActivityManager.RunningAppProcessInfo outInfo,
int clientTargetSdk) {
outInfo.pid = app.pid;
@@ -12142,6 +12144,8 @@
outInfo.importance = procStateToImportance(procState, adj, outInfo, clientTargetSdk);
outInfo.importanceReasonCode = app.adjTypeCode;
outInfo.processState = app.curProcState;
+ outInfo.isFocused = (app == getTopAppLocked());
+ outInfo.lastActivityTime = app.lastActivityTime;
}
@Override
@@ -12172,7 +12176,7 @@
ActivityManager.RunningAppProcessInfo currApp =
new ActivityManager.RunningAppProcessInfo(app.processName,
app.pid, app.getPackageList());
- fillInProcMemInfo(app, currApp, clientTargetSdk);
+ fillInProcMemInfoLocked(app, currApp, clientTargetSdk);
if (app.adjSource instanceof ProcessRecord) {
currApp.importanceReasonPid = ((ProcessRecord)app.adjSource).pid;
currApp.importanceReasonImportance =
@@ -12241,7 +12245,7 @@
proc = mPidsSelfLocked.get(Binder.getCallingPid());
}
if (proc != null) {
- fillInProcMemInfo(proc, outState, clientTargetSdk);
+ fillInProcMemInfoLocked(proc, outState, clientTargetSdk);
}
}
}
@@ -12425,8 +12429,10 @@
}
} else if ("binder-proxies".equals(cmd)) {
if (opti >= args.length) {
+ dumpBinderProxyInterfaceCounts(pw,
+ "Top proxy interface names held by SYSTEM");
dumpBinderProxiesCounts(pw, BinderInternal.nGetBinderProxyPerUidCounts(),
- "Counts of Binder Proxies held by SYSTEM");
+ "Number of proxies per uid held by SYSTEM");
} else {
String uid = args[opti];
opti++;
@@ -12964,6 +12970,15 @@
return printed;
}
+ void dumpBinderProxyInterfaceCounts(PrintWriter pw, String header) {
+ final BinderProxy.InterfaceCount[] proxyCounts = BinderProxy.getSortedInterfaceCounts(50);
+
+ pw.println(header);
+ for (int i = 0; i < proxyCounts.length; i++) {
+ pw.println(" #" + (i + 1) + ": " + proxyCounts[i]);
+ }
+ }
+
boolean dumpBinderProxiesCounts(PrintWriter pw, SparseIntArray counts, String header) {
if(counts != null) {
pw.println(header);
@@ -20697,22 +20712,24 @@
}
@GuardedBy("this")
+ ProcessRecord getTopAppLocked() {
+ final ActivityRecord TOP_ACT = resumedAppLocked();
+ if (TOP_ACT != null && TOP_ACT.hasProcess()) {
+ return (ProcessRecord) TOP_ACT.app.mOwner;
+ } else {
+ return null;
+ }
+ }
+
+ @GuardedBy("this")
final void updateOomAdjLocked() {
mOomAdjProfiler.oomAdjStarted();
- final ActivityRecord TOP_ACT = resumedAppLocked();
- final ProcessRecord TOP_APP = TOP_ACT != null && TOP_ACT.hasProcess()
- ? (ProcessRecord) TOP_ACT.app.mOwner : null;
+ final ProcessRecord TOP_APP = getTopAppLocked();
final long now = SystemClock.uptimeMillis();
final long nowElapsed = SystemClock.elapsedRealtime();
final long oldTime = now - ProcessList.MAX_EMPTY_TIME;
final int N = mLruProcesses.size();
- if (false) {
- RuntimeException e = new RuntimeException();
- e.fillInStackTrace();
- Slog.i(TAG, "updateOomAdj: top=" + TOP_ACT, e);
- }
-
// Reset state in all uid records.
for (int i=mActiveUids.size()-1; i>=0; i--) {
final UidRecord uidRec = mActiveUids.valueAt(i);
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 5105941..84bdbba 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -115,7 +115,6 @@
import com.android.server.connectivity.tethering.IControlsTethering;
import com.android.server.connectivity.tethering.IPv6TetheringCoordinator;
import com.android.server.connectivity.tethering.OffloadController;
-import com.android.server.connectivity.tethering.SimChangeListener;
import com.android.server.connectivity.tethering.TetherInterfaceStateMachine;
import com.android.server.connectivity.tethering.TetheringConfiguration;
import com.android.server.connectivity.tethering.TetheringDependencies;
@@ -201,8 +200,6 @@
// into a single coherent structure.
private final HashSet<TetherInterfaceStateMachine> mForwardedDownstreams;
private final VersionedBroadcastListener mCarrierConfigChange;
- // TODO: Delete SimChangeListener; it's obsolete.
- private final SimChangeListener mSimChange;
private final TetheringDependencies mDeps;
private volatile TetheringConfiguration mConfig;
@@ -252,14 +249,6 @@
updateConfiguration();
reevaluateSimCardProvisioning();
});
- // TODO: Remove SimChangeListener altogether. For now, we retain it
- // for logging purposes in case we need to debug something that might
- // be related to changing signals from ACTION_SIM_STATE_CHANGED to
- // ACTION_CARRIER_CONFIG_CHANGED.
- mSimChange = new SimChangeListener(
- mContext, smHandler, () -> {
- mLog.log("OBSERVED SIM card change");
- });
mStateReceiver = new StateReceiver();
@@ -1530,7 +1519,6 @@
return;
}
- mSimChange.startListening();
mUpstreamNetworkMonitor.start(mDeps.getDefaultNetworkRequest());
// TODO: De-duplicate with updateUpstreamWanted() below.
@@ -1546,7 +1534,6 @@
public void exit() {
mOffload.stop();
mUpstreamNetworkMonitor.stop();
- mSimChange.stopListening();
notifyDownstreamsOfNewUpstreamIface(null);
handleNewUpstreamNetworkState(null);
}
diff --git a/services/core/java/com/android/server/connectivity/tethering/SimChangeListener.java b/services/core/java/com/android/server/connectivity/tethering/SimChangeListener.java
deleted file mode 100644
index 33c9355..0000000
--- a/services/core/java/com/android/server/connectivity/tethering/SimChangeListener.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2017 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.connectivity.tethering;
-
-import static com.android.internal.telephony.IccCardConstants.INTENT_VALUE_ICC_LOADED;
-import static com.android.internal.telephony.IccCardConstants.INTENT_KEY_ICC_STATE;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.net.util.VersionedBroadcastListener;
-import android.net.util.VersionedBroadcastListener.IntentCallback;
-import android.os.Handler;
-import android.util.Log;
-
-import com.android.internal.telephony.TelephonyIntents;
-
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.function.Consumer;
-
-
-/**
- * A utility class that runs the provided callback on the provided handler when
- * observing a new SIM card having been loaded.
- *
- * @hide
- */
-public class SimChangeListener extends VersionedBroadcastListener {
- private static final String TAG = SimChangeListener.class.getSimpleName();
- private static final boolean DBG = false;
-
- public SimChangeListener(Context ctx, Handler handler, Runnable onSimCardLoadedCallback) {
- super(TAG, ctx, handler, makeIntentFilter(), makeCallback(onSimCardLoadedCallback));
- }
-
- private static IntentFilter makeIntentFilter() {
- final IntentFilter filter = new IntentFilter();
- filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
- return filter;
- }
-
- private static Consumer<Intent> makeCallback(Runnable onSimCardLoadedCallback) {
- return new Consumer<Intent>() {
- private boolean mSimNotLoadedSeen = false;
-
- @Override
- public void accept(Intent intent) {
- final String state = intent.getStringExtra(INTENT_KEY_ICC_STATE);
- Log.d(TAG, "got Sim changed to state " + state + ", mSimNotLoadedSeen=" +
- mSimNotLoadedSeen);
-
- if (!INTENT_VALUE_ICC_LOADED.equals(state)) {
- mSimNotLoadedSeen = true;
- return;
- }
-
- if (mSimNotLoadedSeen) {
- mSimNotLoadedSeen = false;
- onSimCardLoadedCallback.run();
- }
- }
- };
- }
-}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index c6a8712..adfa8d5 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -2012,7 +2012,7 @@
enforceShell();
final long origId = Binder.clearCallingIdentity();
try {
- (new LockSettingsShellCommand(mContext, new LockPatternUtils(mContext))).exec(
+ (new LockSettingsShellCommand(new LockPatternUtils(mContext))).exec(
this, in, out, err, args, callback, resultReceiver);
} finally {
Binder.restoreCallingIdentity(origId);
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
index 4d2cf32..085d17d 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
@@ -21,13 +21,13 @@
import static com.android.internal.widget.LockPatternUtils.stringToPattern;
import android.app.ActivityManager;
-import android.content.Context;
-import android.os.RemoteException;
import android.os.ShellCommand;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockPatternUtils.RequestThrottledException;
+import java.io.PrintWriter;
+
class LockSettingsShellCommand extends ShellCommand {
private static final String COMMAND_SET_PATTERN = "set-pattern";
@@ -38,20 +38,22 @@
private static final String COMMAND_SET_DISABLED = "set-disabled";
private static final String COMMAND_VERIFY = "verify";
private static final String COMMAND_GET_DISABLED = "get-disabled";
+ private static final String COMMAND_HELP = "help";
private int mCurrentUserId;
private final LockPatternUtils mLockPatternUtils;
- private final Context mContext;
private String mOld = "";
private String mNew = "";
- LockSettingsShellCommand(Context context, LockPatternUtils lockPatternUtils) {
- mContext = context;
+ LockSettingsShellCommand(LockPatternUtils lockPatternUtils) {
mLockPatternUtils = lockPatternUtils;
}
@Override
public int onCommand(String cmd) {
+ if (cmd == null) {
+ return handleDefaultCommands(cmd);
+ }
try {
mCurrentUserId = ActivityManager.getService().getCurrentUser().id;
@@ -84,6 +86,9 @@
case COMMAND_GET_DISABLED:
runGetDisabled();
break;
+ case COMMAND_HELP:
+ onHelp();
+ break;
default:
getErrPrintWriter().println("Unknown command: " + cmd);
break;
@@ -103,6 +108,43 @@
@Override
public void onHelp() {
+ try (final PrintWriter pw = getOutPrintWriter();) {
+ pw.println("lockSettings service commands:");
+ pw.println("");
+ pw.println("NOTE: when lock screen is set, all commands require the --old <CREDENTIAL>"
+ + " argument.");
+ pw.println("");
+ pw.println(" help");
+ pw.println(" Prints this help text.");
+ pw.println("");
+ pw.println(" get-disabled [--old <CREDENTIAL>] [--user USER_ID]");
+ pw.println(" Checks whether lock screen is disabled.");
+ pw.println("");
+ pw.println(" set-disabled [--old <CREDENTIAL>] [--user USER_ID] <true|false>");
+ pw.println(" When true, disables lock screen.");
+ pw.println("");
+ pw.println(" set-pattern [--old <CREDENTIAL>] [--user USER_ID] <PATTERN>");
+ pw.println(" Sets the lock screen as pattern, using the given PATTERN to unlock.");
+ pw.println("");
+ pw.println(" set-pin [--old <CREDENTIAL>] [--user USER_ID] <PIN>");
+ pw.println(" Sets the lock screen as PIN, using the given PIN to unlock.");
+ pw.println("");
+ pw.println(" set-pin [--old <CREDENTIAL>] [--user USER_ID] <PASSWORD>");
+ pw.println(" Sets the lock screen as password, using the given PASSOWRD to unlock.");
+ pw.println("");
+ pw.println(" sp [--old <CREDENTIAL>] [--user USER_ID]");
+ pw.println(" Gets whether synthetic password is enabled.");
+ pw.println("");
+ pw.println(" sp [--old <CREDENTIAL>] [--user USER_ID] <1|0>");
+ pw.println(" Enables / disables synthetic password.");
+ pw.println("");
+ pw.println(" clear [--old <CREDENTIAL>] [--user USER_ID]");
+ pw.println(" Clears the lock credentials.");
+ pw.println("");
+ pw.println(" verify [--old <CREDENTIAL>] [--user USER_ID]");
+ pw.println(" Verifies the lock credentials.");
+ pw.println("");
+ }
}
private void parseArgs() {
@@ -134,27 +176,27 @@
mLockPatternUtils.isSyntheticPasswordEnabled()));
}
- private void runSetPattern() throws RemoteException {
+ private void runSetPattern() {
mLockPatternUtils.saveLockPattern(stringToPattern(mNew), mOld, mCurrentUserId);
getOutPrintWriter().println("Pattern set to '" + mNew + "'");
}
- private void runSetPassword() throws RemoteException {
+ private void runSetPassword() {
mLockPatternUtils.saveLockPassword(mNew, mOld, PASSWORD_QUALITY_ALPHABETIC, mCurrentUserId);
getOutPrintWriter().println("Password set to '" + mNew + "'");
}
- private void runSetPin() throws RemoteException {
+ private void runSetPin() {
mLockPatternUtils.saveLockPassword(mNew, mOld, PASSWORD_QUALITY_NUMERIC, mCurrentUserId);
getOutPrintWriter().println("Pin set to '" + mNew + "'");
}
- private void runClear() throws RemoteException {
+ private void runClear() {
mLockPatternUtils.clearLock(mOld, mCurrentUserId);
getOutPrintWriter().println("Lock credential cleared");
}
- private void runSetDisabled() throws RemoteException {
+ private void runSetDisabled() {
final boolean disabled = Boolean.parseBoolean(mNew);
mLockPatternUtils.setLockScreenDisabled(disabled, mCurrentUserId);
getOutPrintWriter().println("Lock screen disabled set to " + disabled);
@@ -165,7 +207,7 @@
getOutPrintWriter().println(isLockScreenDisabled);
}
- private boolean checkCredential() throws RemoteException {
+ private boolean checkCredential() {
final boolean havePassword = mLockPatternUtils.isLockPasswordEnabled(mCurrentUserId);
final boolean havePattern = mLockPatternUtils.isLockPatternEnabled(mCurrentUserId);
if (havePassword || havePattern) {
diff --git a/services/robotests/src/android/app/backup/ForwardingBackupAgent.java b/services/robotests/src/android/app/backup/ForwardingBackupAgent.java
new file mode 100644
index 0000000..4ff5b7c
--- /dev/null
+++ b/services/robotests/src/android/app/backup/ForwardingBackupAgent.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.app.backup;
+
+import android.content.Context;
+import android.os.ParcelFileDescriptor;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * Useful for spying in {@link BackupAgent} instances since their {@link BackupAgent#onBind()} is
+ * final and always points to the original instance, instead of the spy.
+ *
+ * <p>To use, construct a spy of the desired {@link BackupAgent}, spying on the methods of interest.
+ * Then, where you need to pass the agent, use {@link ForwardingBackupAgent#forward(BackupAgent)}
+ * with the spy.
+ */
+public class ForwardingBackupAgent extends BackupAgent {
+ /** Returns a {@link BackupAgent} that forwards method calls to {@code backupAgent}. */
+ public static BackupAgent forward(BackupAgent backupAgent) {
+ return new ForwardingBackupAgent(backupAgent);
+ }
+
+ private final BackupAgent mBackupAgent;
+
+ private ForwardingBackupAgent(BackupAgent backupAgent) {
+ mBackupAgent = backupAgent;
+ }
+
+ @Override
+ public void onCreate() {
+ mBackupAgent.onCreate();
+ }
+
+ @Override
+ public void onDestroy() {
+ mBackupAgent.onDestroy();
+ }
+
+ @Override
+ public void onBackup(
+ ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState)
+ throws IOException {
+ mBackupAgent.onBackup(oldState, data, newState);
+ }
+
+ @Override
+ public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState)
+ throws IOException {
+ mBackupAgent.onRestore(data, appVersionCode, newState);
+ }
+
+ @Override
+ public void onRestore(BackupDataInput data, long appVersionCode, ParcelFileDescriptor newState)
+ throws IOException {
+ mBackupAgent.onRestore(data, appVersionCode, newState);
+ }
+
+ @Override
+ public void onFullBackup(FullBackupDataOutput data) throws IOException {
+ mBackupAgent.onFullBackup(data);
+ }
+
+ @Override
+ public void onQuotaExceeded(long backupDataBytes, long quotaBytes) {
+ mBackupAgent.onQuotaExceeded(backupDataBytes, quotaBytes);
+ }
+
+ @Override
+ public void onRestoreFile(
+ ParcelFileDescriptor data, long size, File destination, int type, long mode, long mtime)
+ throws IOException {
+ mBackupAgent.onRestoreFile(data, size, destination, type, mode, mtime);
+ }
+
+ @Override
+ protected void onRestoreFile(
+ ParcelFileDescriptor data,
+ long size,
+ int type,
+ String domain,
+ String path,
+ long mode,
+ long mtime)
+ throws IOException {
+ mBackupAgent.onRestoreFile(data, size, type, domain, path, mode, mtime);
+ }
+
+ @Override
+ public void onRestoreFinished() {
+ mBackupAgent.onRestoreFinished();
+ }
+
+ @Override
+ public void attach(Context context) {
+ mBackupAgent.attach(context);
+ }
+}
diff --git a/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java b/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
index d16bc26..abaed7c 100644
--- a/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
+++ b/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
@@ -16,7 +16,7 @@
package com.android.server.backup;
-import static com.android.server.backup.testing.BackupManagerServiceTestUtils.startBackupThread;
+import static com.android.server.backup.testing.BackupManagerServiceTestUtils.startSilentBackupThread;
import static com.android.server.backup.testing.TransportData.backupTransport;
import static com.android.server.backup.testing.TransportData.d2dTransport;
import static com.android.server.backup.testing.TransportData.localTransport;
@@ -46,6 +46,7 @@
import android.platform.test.annotations.Presubmit;
import android.provider.Settings;
import com.android.server.backup.internal.BackupRequest;
+import com.android.server.backup.testing.BackupManagerServiceTestUtils;
import com.android.server.backup.testing.TransportData;
import com.android.server.backup.testing.TransportTestUtils.TransportMock;
import com.android.server.backup.transport.TransportNotRegisteredException;
@@ -68,7 +69,6 @@
import org.robolectric.annotation.Config;
import org.robolectric.annotation.Implements;
import org.robolectric.shadows.ShadowContextWrapper;
-import org.robolectric.shadows.ShadowLog;
import org.robolectric.shadows.ShadowLooper;
import org.robolectric.shadows.ShadowPackageManager;
import org.robolectric.shadows.ShadowSettings;
@@ -104,7 +104,10 @@
mTransport = backupTransport();
mTransportName = mTransport.transportName;
- mBackupThread = startBackupThread(this::uncaughtException);
+ // Unrelated exceptions are thrown in the backup thread. Until we mock everything properly
+ // we should not fail tests because of this. This is not flakiness, the exceptions thrown
+ // don't interfere with the tests.
+ mBackupThread = startSilentBackupThread(TAG);
mShadowBackupLooper = shadowOf(mBackupThread.getLooper());
ContextWrapper context = RuntimeEnvironment.application;
@@ -113,8 +116,10 @@
mShadowContext = shadowOf(context);
File cacheDir = mContext.getCacheDir();
- mBaseStateDir = new File(cacheDir, "base_state_dir");
- mDataDir = new File(cacheDir, "data_dir");
+ // Corresponds to /data/backup
+ mBaseStateDir = new File(cacheDir, "base_state");
+ // Corresponds to /cache/backup_stage
+ mDataDir = new File(cacheDir, "data");
ShadowBackupPolicyEnforcer.setMandatoryBackupTransport(null);
}
@@ -126,13 +131,6 @@
ShadowBackupPolicyEnforcer.setMandatoryBackupTransport(null);
}
- private void uncaughtException(Thread thread, Throwable e) {
- // Unrelated exceptions are thrown in the backup thread. Until we mock everything properly
- // we should not fail tests because of this. This is not flakiness, the exceptions thrown
- // don't interfere with the tests.
- ShadowLog.e(TAG, "Uncaught exception in test thread " + thread.getName(), e);
- }
-
/* Tests for destination string */
@Test
@@ -864,22 +862,8 @@
}
private BackupManagerService createInitializedBackupManagerService() {
- BackupManagerService backupManagerService =
- new BackupManagerService(
- mContext,
- new Trampoline(mContext),
- mBackupThread,
- mBaseStateDir,
- mDataDir,
- mTransportManager);
- mShadowBackupLooper.runToEndOfTasks();
- // Handler instances have their own clock, so advancing looper (with runToEndOfTasks())
- // above does NOT advance the handlers' clock, hence whenever a handler post messages with
- // specific time to the looper the time of those messages will be before the looper's time.
- // To fix this we advance SystemClock as well since that is from where the handlers read
- // time.
- ShadowSystemClock.setCurrentTimeMillis(mShadowBackupLooper.getScheduler().getCurrentTime());
- return backupManagerService;
+ return BackupManagerServiceTestUtils.createInitializedBackupManagerService(
+ mContext, mBackupThread, mBaseStateDir, mDataDir, mTransportManager);
}
private void setUpPowerManager(BackupManagerService backupManagerService) {
diff --git a/services/robotests/src/com/android/server/backup/PerformBackupTaskTest.java b/services/robotests/src/com/android/server/backup/PerformBackupTaskTest.java
index 88a51a5..2d6dd4d 100644
--- a/services/robotests/src/com/android/server/backup/PerformBackupTaskTest.java
+++ b/services/robotests/src/com/android/server/backup/PerformBackupTaskTest.java
@@ -16,12 +16,17 @@
package com.android.server.backup;
+import static android.app.backup.ForwardingBackupAgent.forward;
+
+import static com.android.server.backup.BackupManagerService.PACKAGE_MANAGER_SENTINEL;
import static com.android.server.backup.testing.BackupManagerServiceTestUtils.createBackupWakeLock;
+import static com.android.server.backup.testing.BackupManagerServiceTestUtils.createInitializedBackupManagerService;
import static com.android.server.backup.testing.BackupManagerServiceTestUtils.setUpBackupManagerServiceBasics;
-import static com.android.server.backup.testing.BackupManagerServiceTestUtils.startBackupThreadAndGetLooper;
import static com.android.server.backup.testing.TestUtils.uncheck;
import static com.android.server.backup.testing.TransportData.backupTransport;
import static com.google.common.truth.Truth.assertThat;
+
+import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
import static java.util.Collections.emptyList;
import static java.util.stream.Collectors.toCollection;
import static java.util.stream.Collectors.toList;
@@ -34,6 +39,7 @@
import static org.mockito.ArgumentMatchers.intThat;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
@@ -51,12 +57,12 @@
import android.app.backup.IBackupManager;
import android.app.backup.IBackupManagerMonitor;
import android.app.backup.IBackupObserver;
+import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.DeadObjectException;
import android.os.Handler;
-import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.PowerManager;
@@ -77,13 +83,23 @@
import com.android.server.testing.SystemLoaderPackages;
import com.android.server.testing.shadows.ShadowBackupDataInput;
import com.android.server.testing.shadows.ShadowBackupDataOutput;
+
+import com.google.common.truth.IterableSubject;
+
+import java.io.ByteArrayOutputStream;
import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.OutputStream;
import java.nio.file.Files;
-import java.nio.file.Paths;
+import java.nio.file.Path;
import java.util.ArrayList;
+import java.util.Iterator;
import java.util.List;
import java.util.stream.Stream;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -98,6 +114,19 @@
import org.robolectric.shadows.ShadowPackageManager;
import org.robolectric.shadows.ShadowQueuedWork;
+// TODO: Don't do backup for full-backup
+// TODO: Don't do backup for stopped
+// TODO: Don't do backup for non-eligible
+// TODO: (performBackup() => SUCCESS, finishBackup() => SUCCESS) => delete stage file, renames
+// state file
+// TODO: Check agent writes state file => check file content
+// TODO: Check agent writes new state file => next agent reads it correctly
+// TODO: Check non-incremental has empty state file
+// TODO: Check queue of 2, transport rejecting package but other package proceeds
+// TODO: Check queue in general, behavior w/ multiple packages
+// TODO: Check quota is passed from transport to agent
+// TODO: Check non-incremental and transport requests PM in queue
+// TODO: Verify initialization
@RunWith(FrameworkRobolectricTestRunner.class)
@Config(
manifest = Config.NONE,
@@ -114,20 +143,22 @@
private static final String PACKAGE_1 = "com.example.package1";
private static final String PACKAGE_2 = "com.example.package2";
- @Mock private BackupManagerService mBackupManagerService;
@Mock private TransportManager mTransportManager;
@Mock private DataChangedJournal mDataChangedJournal;
@Mock private IBackupObserver mObserver;
@Mock private IBackupManagerMonitor mMonitor;
@Mock private OnTaskFinishedListener mListener;
+ private BackupManagerService mBackupManagerService;
private TransportData mTransport;
private ShadowLooper mShadowBackupLooper;
- private BackupHandler mBackupHandler;
+ private Handler mBackupHandler;
private PowerManager.WakeLock mWakeLock;
private ShadowPackageManager mShadowPackageManager;
private FakeIBackupManager mBackupManager;
private File mBaseStateDir;
+ private File mDataDir;
private Application mApplication;
+ private Context mContext;
@Before
public void setUp() throws Exception {
@@ -136,49 +167,269 @@
mTransport = backupTransport();
mApplication = RuntimeEnvironment.application;
+ mContext = mApplication;
+
File cacheDir = mApplication.getCacheDir();
- mBaseStateDir = new File(cacheDir, "base_state_dir");
- File dataDir = new File(cacheDir, "data_dir");
- assertThat(mBaseStateDir.mkdir()).isTrue();
- assertThat(dataDir.mkdir()).isTrue();
+ // Corresponds to /data/backup
+ mBaseStateDir = new File(cacheDir, "base_state");
+ // Corresponds to /cache/backup_stage
+ mDataDir = new File(cacheDir, "data");
+ // We create here simulating init.rc
+ mDataDir.mkdirs();
+ assertThat(mDataDir.isDirectory()).isTrue();
PackageManager packageManager = mApplication.getPackageManager();
mShadowPackageManager = shadowOf(packageManager);
mWakeLock = createBackupWakeLock(mApplication);
- Looper backupLooper = startBackupThreadAndGetLooper();
- mShadowBackupLooper = shadowOf(backupLooper);
-
- Handler mainHandler = new Handler(Looper.getMainLooper());
- BackupAgentTimeoutParameters agentTimeoutParameters =
- new BackupAgentTimeoutParameters(mainHandler, mApplication.getContentResolver());
- agentTimeoutParameters.start();
-
- // We need to mock BMS timeout parameters before initializing the BackupHandler since
- // the constructor of BackupHandler relies on the timeout parameters.
- when(mBackupManagerService.getAgentTimeoutParameters()).thenReturn(agentTimeoutParameters);
- mBackupHandler = new BackupHandler(mBackupManagerService, backupLooper);
-
mBackupManager = spy(FakeIBackupManager.class);
- BackupManagerConstants constants =
- new BackupManagerConstants(mainHandler, mApplication.getContentResolver());
- constants.start();
-
+ mBackupManagerService =
+ spy(
+ createInitializedBackupManagerService(
+ mContext, mBaseStateDir, mDataDir, mTransportManager));
setUpBackupManagerServiceBasics(
mBackupManagerService,
mApplication,
mTransportManager,
packageManager,
- mBackupHandler,
+ mBackupManagerService.getBackupHandler(),
mWakeLock,
- agentTimeoutParameters);
+ mBackupManagerService.getAgentTimeoutParameters());
when(mBackupManagerService.getBaseStateDir()).thenReturn(mBaseStateDir);
- when(mBackupManagerService.getDataDir()).thenReturn(dataDir);
+ when(mBackupManagerService.getDataDir()).thenReturn(mDataDir);
when(mBackupManagerService.getBackupManagerBinder()).thenReturn(mBackupManager);
- when(mBackupManagerService.getConstants()).thenReturn(constants);
- when(mBackupManagerService.makeMetadataAgent()).thenAnswer(invocation -> createPmAgent());
+
+ mBackupHandler = mBackupManagerService.getBackupHandler();
+ mShadowBackupLooper = shadowOf(mBackupHandler.getLooper());
+ }
+
+ @Test
+ public void testRunTask_whenQueueEmpty() throws Exception {
+ when(mBackupManagerService.getCurrentToken()).thenReturn(0L);
+ TransportMock transportMock = setUpTransport(mTransport);
+ PerformBackupTask task =
+ createPerformBackupTask(
+ transportMock.transportClient, mTransport.transportDirName, true);
+
+ runTask(task);
+
+ assertThat(mBackupManagerService.getPendingInits()).isEmpty();
+ assertThat(mBackupManagerService.isBackupRunning()).isFalse();
+ assertThat(mBackupManagerService.getCurrentOperations().size()).isEqualTo(0);
+ assertThat(mWakeLock.isHeld()).isFalse();
+ assertDirectory(getStateDirectory(mTransport)).isEmpty();
+ assertDirectory(mDataDir.toPath()).isEmpty();
+ verify(transportMock.transport, never()).initializeDevice();
+ verify(transportMock.transport, never()).performBackup(any(), any(), anyInt());
+ verify(transportMock.transport, never()).finishBackup();
+ verify(mDataChangedJournal).delete();
+ verify(mListener).onFinished(any());
+ verify(mObserver, never()).onResult(any(), anyInt());
+ verify(mObserver).backupFinished(BackupManager.SUCCESS);
+ // TODO: Verify set current token?
+ }
+
+ @Test
+ public void testRunTask_whenQueueEmpty_doesNotChangeStateFiles() throws Exception {
+ TransportMock transportMock = setUpTransport(mTransport);
+ PerformBackupTask task =
+ createPerformBackupTask(
+ transportMock.transportClient, mTransport.transportDirName, true);
+ createPmStateFile();
+ Files.write(getStateFile(mTransport, PACKAGE_1), "packageState".getBytes());
+
+ runTask(task);
+
+ assertThat(Files.readAllBytes(getStateFile(mTransport, PACKAGE_MANAGER_SENTINEL))).isEqualTo("pmState".getBytes());
+ assertThat(Files.readAllBytes(getStateFile(mTransport, PACKAGE_1))).isEqualTo("packageState".getBytes());
+ }
+
+ @Test
+ public void testRunTask_whenSinglePackage_aboutAgent() throws Exception {
+ TransportMock transportMock = setUpTransport(mTransport);
+ AgentMock agentMock = setUpAgent(PACKAGE_1);
+ agentOnBackupDo(
+ agentMock,
+ (oldState, dataOutput, newState) -> {
+ writeData(dataOutput, "key", "data".getBytes());
+ writeState(newState, "newState".getBytes());
+ });
+ PerformBackupTask task =
+ createPerformBackupTask(
+ transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
+
+ runTask(task);
+
+ verify(agentMock.agent).onBackup(any(), any(), any());
+ assertThat(Files.readAllBytes(getStateFile(mTransport, PACKAGE_1)))
+ .isEqualTo("newState".getBytes());
+ }
+
+ @Test
+ public void testRunTask_whenSinglePackage_notifiesCorrectly() throws Exception {
+ TransportMock transportMock = setUpTransport(mTransport);
+ setUpAgentWithData(PACKAGE_1);
+ PerformBackupTask task =
+ createPerformBackupTask(
+ transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
+
+ runTask(task);
+
+ verify(mBackupManagerService).logBackupComplete(PACKAGE_1);
+ verify(mObserver).onResult(PACKAGE_1, BackupManager.SUCCESS);
+ verify(mListener).onFinished(any());
+ verify(mObserver).backupFinished(BackupManager.SUCCESS);
+ }
+
+ @Test
+ public void testRunTask_whenSinglePackage_releasesWakeLock() throws Exception {
+ TransportMock transportMock = setUpTransport(mTransport);
+ setUpAgentWithData(PACKAGE_1);
+ PerformBackupTask task =
+ createPerformBackupTask(
+ transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
+
+ runTask(task);
+
+ assertThat(mWakeLock.isHeld()).isFalse();
+ }
+
+ @Test
+ public void testRunTask_whenSinglePackage_updatesBookkeeping() throws Exception {
+ TransportMock transportMock = setUpTransport(mTransport);
+ setUpAgentWithData(PACKAGE_1);
+ PerformBackupTask task =
+ createPerformBackupTask(
+ transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
+
+ runTask(task);
+
+ assertThat(mBackupManagerService.getPendingInits()).isEmpty();
+ assertThat(mBackupManagerService.isBackupRunning()).isFalse();
+ assertThat(mBackupManagerService.getCurrentOperations().size()).isEqualTo(0);
+ verify(mDataChangedJournal).delete();
+ }
+
+ @Test
+ public void testRunTask_whenSinglePackageIncremental_passesOldStateToAgent() throws Exception {
+ TransportMock transportMock = setUpTransport(mTransport);
+ AgentMock agentMock = setUpAgentWithData(PACKAGE_1);
+ PerformBackupTask task =
+ createPerformBackupTask(
+ transportMock.transportClient,
+ mTransport.transportDirName,
+ false,
+ PACKAGE_1);
+ createPmStateFile();
+ Files.write(getStateFile(mTransport, PACKAGE_1), "oldState".getBytes());
+
+ runTask(task);
+
+ assertThat(agentMock.oldState).isEqualTo("oldState".getBytes());
+ }
+
+ @Test
+ public void testRunTask_whenSinglePackageNonIncremental_passesEmptyOldStateToAgent() throws Exception {
+ TransportMock transportMock = setUpTransport(mTransport);
+ AgentMock agentMock = setUpAgentWithData(PACKAGE_1);
+ PerformBackupTask task =
+ createPerformBackupTask(
+ transportMock.transportClient,
+ mTransport.transportDirName,
+ true,
+ PACKAGE_1);
+ createPmStateFile();
+ Files.write(getStateFile(mTransport, PACKAGE_1), "oldState".getBytes());
+
+ runTask(task);
+
+ assertThat(agentMock.oldState).isEqualTo(new byte[0]);
+ }
+
+ @Test
+ public void testRunTask_whenSinglePackageNonIncremental_doesNotBackUpPm() throws Exception {
+ PackageManagerBackupAgent pmAgent = spy(createPmAgent());
+ when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent));
+ TransportMock transportMock = setUpTransport(mTransport);
+ setUpAgentWithData(PACKAGE_1);
+ PerformBackupTask task =
+ createPerformBackupTask(
+ transportMock.transportClient,
+ mTransport.transportDirName,
+ true,
+ PACKAGE_1);
+
+ runTask(task);
+
+ verify(pmAgent, never()).onBackup(any(), any(), any());
+ }
+
+ @Test
+ public void testRunTask_whenPackageAndPmNonIncremental_backsUpPm() throws Exception {
+ PackageManagerBackupAgent pmAgent = spy(createPmAgent());
+ when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent));
+ TransportMock transportMock = setUpTransport(mTransport);
+ setUpAgentWithData(PACKAGE_1);
+ PerformBackupTask task =
+ createPerformBackupTask(
+ transportMock.transportClient,
+ mTransport.transportDirName,
+ true,
+ PACKAGE_1,
+ PACKAGE_MANAGER_SENTINEL);
+
+ runTask(task);
+
+ verify(pmAgent).onBackup(any(), any(), any());
+ }
+
+ @Test
+ public void testRunTask_whenSinglePackageIncremental_backsUpPm() throws Exception {
+ PackageManagerBackupAgent pmAgent = spy(createPmAgent());
+ when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent));
+ TransportMock transportMock = setUpTransport(mTransport);
+ setUpAgentWithData(PACKAGE_1);
+ PerformBackupTask task =
+ createPerformBackupTask(
+ transportMock.transportClient,
+ mTransport.transportDirName,
+ false,
+ PACKAGE_1);
+
+ runTask(task);
+
+ verify(pmAgent).onBackup(any(), any(), any());
+ }
+
+ @Test
+ public void testRunTask_whenSinglePackageNoPmState_initializesTransport() throws Exception {
+ TransportMock transportMock = setUpTransport(mTransport);
+ setUpAgentWithData(PACKAGE_1);
+ PerformBackupTask task =
+ createPerformBackupTask(
+ transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
+ Files.deleteIfExists(getStateFile(mTransport, PACKAGE_MANAGER_SENTINEL));
+
+ runTask(task);
+
+ verify(transportMock.transport).initializeDevice();
+ }
+
+ @Test
+ public void testRunTask_whenSinglePackageWithPmState_doesNotInitializeTransport()
+ throws Exception {
+ TransportMock transportMock = setUpTransport(mTransport);
+ setUpAgentWithData(PACKAGE_1);
+ PerformBackupTask task =
+ createPerformBackupTask(
+ transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
+ createPmStateFile();
+
+ runTask(task);
+
+ verify(transportMock.transport, never()).initializeDevice();
}
@Test
@@ -249,20 +500,6 @@
}
@Test
- public void testRunTask_callsListenerAndObserver() throws Exception {
- TransportMock transportMock = setUpTransport(mTransport);
- setUpAgent(PACKAGE_1);
- PerformBackupTask task =
- createPerformBackupTask(
- transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
-
- runTask(task);
-
- verify(mListener).onFinished(any());
- verify(mObserver).backupFinished(eq(BackupManager.SUCCESS));
- }
-
- @Test
public void testRunTask_releasesWakeLock() throws Exception {
TransportMock transportMock = setUpTransport(mTransport);
setUpAgent(PACKAGE_1);
@@ -281,7 +518,7 @@
IBackupTransport transportBinder = transportMock.transport;
AgentMock agentMock = setUpAgent(PACKAGE_1);
agentOnBackupDo(
- agentMock.agent,
+ agentMock,
(oldState, dataOutput, newState) -> {
writeData(dataOutput, "key1", "foo".getBytes());
writeData(dataOutput, "key2", "bar".getBytes());
@@ -289,43 +526,48 @@
PerformBackupTask task =
createPerformBackupTask(
transportMock.transportClient, mTransport.transportDirName, PACKAGE_1);
- // We need to verify at call time because the file is deleted right after
+ Path backupDataPath =
+ Files.createTempFile(mContext.getCacheDir().toPath(), "backup", ".tmp");
when(transportBinder.performBackup(argThat(packageInfo(PACKAGE_1)), any(), anyInt()))
- .then(this::mockAndVerifyTransportPerformBackupData);
+ .then(
+ invocation -> {
+ ParcelFileDescriptor backupDataParcelFd = invocation.getArgument(1);
+ FileDescriptor backupDataFd = backupDataParcelFd.getFileDescriptor();
+ Files.copy(
+ new FileInputStream(backupDataFd),
+ backupDataPath,
+ REPLACE_EXISTING);
+ backupDataParcelFd.close();
+ return BackupTransport.TRANSPORT_OK;
+ });
runTask(task);
- // Already verified data in mockAndVerifyPerformBackupData
verify(transportBinder).performBackup(argThat(packageInfo(PACKAGE_1)), any(), anyInt());
- }
- private int mockAndVerifyTransportPerformBackupData(InvocationOnMock invocation)
- throws IOException {
- ParcelFileDescriptor data = invocation.getArgument(1);
-
- // Verifying that what we passed to the transport is what the agent wrote
- BackupDataInput dataInput = new BackupDataInput(data.getFileDescriptor());
+ // Now verify data sent
+ FileInputStream inputStream = new FileInputStream(backupDataPath.toFile());
+ BackupDataInput backupData = new BackupDataInput(inputStream.getFD());
// "key1" => "foo"
- assertThat(dataInput.readNextHeader()).isTrue();
- assertThat(dataInput.getKey()).isEqualTo("key1");
- int size1 = dataInput.getDataSize();
+ assertThat(backupData.readNextHeader()).isTrue();
+ assertThat(backupData.getKey()).isEqualTo("key1");
+ int size1 = backupData.getDataSize();
byte[] data1 = new byte[size1];
- dataInput.readEntityData(data1, 0, size1);
+ backupData.readEntityData(data1, 0, size1);
assertThat(data1).isEqualTo("foo".getBytes());
// "key2" => "bar"
- assertThat(dataInput.readNextHeader()).isTrue();
- assertThat(dataInput.getKey()).isEqualTo("key2");
- int size2 = dataInput.getDataSize();
+ assertThat(backupData.readNextHeader()).isTrue();
+ assertThat(backupData.getKey()).isEqualTo("key2");
+ int size2 = backupData.getDataSize();
byte[] data2 = new byte[size2];
- dataInput.readEntityData(data2, 0, size2);
+ backupData.readEntityData(data2, 0, size2);
assertThat(data2).isEqualTo("bar".getBytes());
// No more
- assertThat(dataInput.readNextHeader()).isFalse();
-
- return BackupTransport.TRANSPORT_OK;
+ assertThat(backupData.readNextHeader()).isFalse();
+ inputStream.close();
}
@Test
@@ -350,7 +592,7 @@
TransportMock transportMock = setUpTransport(mTransport);
AgentMock agentMock = setUpAgent(PACKAGE_1);
agentOnBackupDo(
- agentMock.agent,
+ agentMock,
(oldState, dataOutput, newState) -> {
char prohibitedChar = 0xff00;
writeData(dataOutput, prohibitedChar + "key", "foo".getBytes());
@@ -374,13 +616,13 @@
AgentMock agentMock1 = agentMocks.get(0);
AgentMock agentMock2 = agentMocks.get(1);
agentOnBackupDo(
- agentMock1.agent,
+ agentMock1,
(oldState, dataOutput, newState) -> {
char prohibitedChar = 0xff00;
writeData(dataOutput, prohibitedChar + "key", "foo".getBytes());
});
agentOnBackupDo(
- agentMock2.agent,
+ agentMock2,
(oldState, dataOutput, newState) -> {
writeData(dataOutput, "key", "bar".getBytes());
});
@@ -528,6 +770,7 @@
runTask(task);
+ // Error because it was non-incremental already, so transport can't request it
verify(mObserver).onResult(PACKAGE_1, BackupManager.ERROR_TRANSPORT_ABORTED);
verify(mObserver).backupFinished(BackupManager.ERROR_TRANSPORT_ABORTED);
}
@@ -553,10 +796,9 @@
mTransport.transportDirName,
false,
PACKAGE_1);
- // Write content to be incremental
- Files.write(
- Paths.get(mBaseStateDir.getAbsolutePath(), mTransport.transportDirName, PACKAGE_1),
- "existent".getBytes());
+ createPmStateFile();
+ // Write state to be incremental
+ Files.write(getStateFile(mTransport, PACKAGE_1), "oldState".getBytes());
runTask(task);
@@ -566,25 +808,12 @@
}
@Test
- public void testRunTask_whenQueueEmpty() throws Exception {
- TransportMock transportMock = setUpTransport(mTransport);
- PerformBackupTask task =
- createPerformBackupTask(
- transportMock.transportClient, mTransport.transportDirName, true);
-
- runTask(task);
-
- verify(mObserver).backupFinished(eq(BackupManager.SUCCESS));
- }
-
- @Test
public void testRunTask_whenIncrementalAndTransportUnavailableDuringPmBackup()
throws Exception {
TransportMock transportMock = setUpTransport(mTransport);
IBackupTransport transportBinder = transportMock.transport;
setUpAgent(PACKAGE_1);
- when(transportBinder.getBackupQuota(
- eq(BackupManagerService.PACKAGE_MANAGER_SENTINEL), anyBoolean()))
+ when(transportBinder.getBackupQuota(eq(PACKAGE_MANAGER_SENTINEL), anyBoolean()))
.thenThrow(DeadObjectException.class);
PerformBackupTask task =
createPerformBackupTask(
@@ -600,9 +829,10 @@
}
@Test
- public void testRunTask_whenIncrementalAndAgentFails() throws Exception {
+ public void testRunTask_whenIncrementalAndPmAgentFails() throws Exception {
TransportMock transportMock = setUpTransport(mTransport);
- createFakePmAgent();
+ PackageManagerBackupAgent pmAgent = createThrowingPmAgent();
+ when(mBackupManagerService.makeMetadataAgent()).thenReturn(pmAgent);
PerformBackupTask task =
createPerformBackupTask(
transportMock.transportClient,
@@ -627,11 +857,18 @@
private TransportMock setUpTransport(TransportData transport) throws Exception {
TransportMock transportMock =
TransportTestUtils.setUpTransport(mTransportManager, transport);
- File stateDir = new File(mBaseStateDir, transport.transportDirName);
- assertThat(stateDir.mkdir()).isTrue();
+ Files.createDirectories(getStateDirectory(transport));
return transportMock;
}
+ private Path getStateDirectory(TransportData transport) {
+ return mBaseStateDir.toPath().resolve(transport.transportDirName);
+ }
+
+ private Path getStateFile(TransportData transport, String packageName) {
+ return getStateDirectory(transport).resolve(packageName);
+ }
+
private List<AgentMock> setUpAgents(String... packageNames) {
return Stream.of(packageNames).map(this::setUpAgent).collect(toList());
}
@@ -652,9 +889,9 @@
spy(IBackupAgent.Stub.asInterface(backupAgent.onBind()));
// Don't crash our only process (in production code this would crash the app, not us)
doNothing().when(backupAgentBinder).fail(any());
- when(mBackupManagerService.bindToAgentSynchronous(
- eq(packageInfo.applicationInfo), anyInt()))
- .thenReturn(backupAgentBinder);
+ doReturn(backupAgentBinder)
+ .when(mBackupManagerService)
+ .bindToAgentSynchronous(eq(packageInfo.applicationInfo), anyInt());
return new AgentMock(backupAgentBinder, backupAgent);
} catch (RemoteException e) {
// Never happens, compiler happy
@@ -668,12 +905,15 @@
private AgentMock setUpAgentWithData(String packageName) {
AgentMock agentMock = setUpAgent(packageName);
+
uncheck(
() ->
agentOnBackupDo(
- agentMock.agent,
- (oldState, dataOutput, newState) ->
- writeData(dataOutput, "key", packageName.getBytes())));
+ agentMock,
+ (oldState, dataOutput, newState) -> {
+ writeData(dataOutput, "key", ("data" + packageName).getBytes());
+ writeState(newState, ("state" + packageName).getBytes());
+ }));
return agentMock;
}
@@ -719,13 +959,12 @@
* Returns an implementation of PackageManagerBackupAgent that throws RuntimeException in {@link
* BackupAgent#onBackup(ParcelFileDescriptor, BackupDataOutput, ParcelFileDescriptor)}
*/
- private PackageManagerBackupAgent createFakePmAgent() {
- PackageManagerBackupAgent fakePmAgent =
- new FakePackageManagerBackupAgent(mApplication.getPackageManager());
- fakePmAgent.attach(mApplication);
- fakePmAgent.onCreate();
- when(mBackupManagerService.makeMetadataAgent()).thenReturn(fakePmAgent);
- return fakePmAgent;
+ private PackageManagerBackupAgent createThrowingPmAgent() {
+ PackageManagerBackupAgent pmAgent =
+ new ThrowingPackageManagerBackupAgent(mApplication.getPackageManager());
+ pmAgent.attach(mApplication);
+ pmAgent.onCreate();
+ return pmAgent;
}
/** Matches {@link PackageInfo} whose package name is {@code packageName}. */
@@ -751,9 +990,49 @@
dataOutput.writeEntityData(data, data.length);
}
- private static void agentOnBackupDo(BackupAgent agent, BackupAgentOnBackup function)
+ private static void writeState(ParcelFileDescriptor newState, byte[] state) throws IOException {
+ OutputStream outputStream = new FileOutputStream(newState.getFileDescriptor());
+ outputStream.write(state);
+ outputStream.flush();
+ }
+
+ /** Prevents the states from being reset and transport initialization. */
+ private void createPmStateFile() throws IOException {
+ Files.write(getStateFile(mTransport, PACKAGE_MANAGER_SENTINEL), "pmState".getBytes());
+ }
+
+ /**
+ * Implements {@code function} for {@link BackupAgent#onBackup(ParcelFileDescriptor,
+ * BackupDataOutput, ParcelFileDescriptor)} of {@code agentMock} and populates {@link
+ * AgentMock#oldState}.
+ */
+ private static void agentOnBackupDo(AgentMock agentMock, BackupAgentOnBackup function)
throws Exception {
- doAnswer(function).when(agent).onBackup(any(), any(), any());
+ doAnswer(
+ (BackupAgentOnBackup)
+ (oldState, dataOutput, newState) -> {
+ ByteArrayOutputStream outputStream =
+ new ByteArrayOutputStream();
+ Utils.transferStreamedData(
+ new FileInputStream(oldState.getFileDescriptor()),
+ outputStream);
+ agentMock.oldState = outputStream.toByteArray();
+ function.onBackup(oldState, dataOutput, newState);
+ })
+ .when(agentMock.agent)
+ .onBackup(any(), any(), any());
+ }
+
+ // TODO: Find some implementation? Extract?
+ private static <T> Iterable<T> oneTimeIterable(Iterator<T> iterator) {
+ return () -> iterator;
+ }
+
+ private static IterableSubject<
+ ? extends IterableSubject<?, Path, Iterable<Path>>, Path, Iterable<Path>>
+ assertDirectory(Path directory) throws IOException {
+ return assertThat(oneTimeIterable(Files.newDirectoryStream(directory).iterator()))
+ .named("directory " + directory);
}
@FunctionalInterface
@@ -777,6 +1056,7 @@
private static class AgentMock {
private final IBackupAgent agentBinder;
private final BackupAgent agent;
+ private byte[] oldState;
private AgentMock(IBackupAgent agentBinder, BackupAgent agent) {
this.agentBinder = agentBinder;
@@ -805,8 +1085,8 @@
}
}
- private static class FakePackageManagerBackupAgent extends PackageManagerBackupAgent {
- public FakePackageManagerBackupAgent(PackageManager packageMgr) {
+ private static class ThrowingPackageManagerBackupAgent extends PackageManagerBackupAgent {
+ ThrowingPackageManagerBackupAgent(PackageManager packageMgr) {
super(packageMgr);
}
diff --git a/services/robotests/src/com/android/server/backup/Utils.java b/services/robotests/src/com/android/server/backup/Utils.java
new file mode 100644
index 0000000..7cdca17
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/Utils.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.backup;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public class Utils {
+ public static final int BUFFER_SIZE = 8192;
+
+ public static void transferStreamedData(InputStream in, OutputStream out) throws IOException {
+ transferStreamedData(in, out, BUFFER_SIZE);
+ }
+
+ public static void transferStreamedData(InputStream in, OutputStream out, int bufferSize)
+ throws IOException {
+ byte[] buffer = new byte[bufferSize];
+ int read;
+ while ((read = in.read(buffer)) != -1) {
+ out.write(buffer, 0, read);
+ }
+ }
+
+ private Utils() {}
+}
diff --git a/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java b/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
index 92d6bbd..c51b75b 100644
--- a/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
+++ b/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
@@ -77,10 +77,9 @@
@RunWith(FrameworkRobolectricTestRunner.class)
@Config(
- manifest = Config.NONE,
- sdk = 26,
- shadows = {ShadowEventLog.class, ShadowPerformUnifiedRestoreTask.class, ShadowBinder.class}
-)
+ manifest = Config.NONE,
+ sdk = 26,
+ shadows = {ShadowEventLog.class, ShadowPerformUnifiedRestoreTask.class, ShadowBinder.class})
@SystemLoaderPackages({"com.android.server.backup"})
@Presubmit
public class ActiveRestoreSessionTest {
@@ -130,6 +129,7 @@
mWakeLock = createBackupWakeLock(application);
+ // TODO: Migrate to use spy(createInitializedBackupManagerService())
setUpBackupManagerServiceBasics(
mBackupManagerService,
application,
diff --git a/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java b/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
index 5a886e3..522578f 100644
--- a/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
+++ b/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
@@ -16,13 +16,20 @@
package com.android.server.backup.testing;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import static org.robolectric.Shadows.shadowOf;
+import android.annotation.Nullable;
import android.app.Application;
import android.app.IActivityManager;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.PowerManager;
@@ -30,32 +37,118 @@
import com.android.server.backup.BackupAgentTimeoutParameters;
import com.android.server.backup.BackupManagerService;
+import com.android.server.backup.Trampoline;
import com.android.server.backup.TransportManager;
-import com.android.server.backup.internal.BackupHandler;
+import com.android.server.backup.internal.Operation;
+import org.mockito.stubbing.Answer;
+import org.robolectric.shadows.ShadowLog;
+import org.robolectric.shadows.ShadowLooper;
+import org.robolectric.shadows.ShadowSystemClock;
+
+import java.io.File;
import java.lang.Thread.UncaughtExceptionHandler;
+import java.util.concurrent.atomic.AtomicReference;
/** Test utils for {@link BackupManagerService} and friends. */
public class BackupManagerServiceTestUtils {
- /** Sets up basic mocks for {@link BackupManagerService}. */
+ public static BackupManagerService createInitializedBackupManagerService(
+ Context context, File baseStateDir, File dataDir, TransportManager transportManager) {
+ return createInitializedBackupManagerService(
+ context,
+ startBackupThread(null),
+ baseStateDir,
+ dataDir,
+ transportManager);
+ }
+
+ public static BackupManagerService createInitializedBackupManagerService(
+ Context context,
+ HandlerThread backupThread,
+ File baseStateDir,
+ File dataDir,
+ TransportManager transportManager) {
+ BackupManagerService backupManagerService =
+ new BackupManagerService(
+ context,
+ new Trampoline(context),
+ backupThread,
+ baseStateDir,
+ dataDir,
+ transportManager);
+ ShadowLooper shadowBackupLooper = shadowOf(backupThread.getLooper());
+ shadowBackupLooper.runToEndOfTasks();
+ // Handler instances have their own clock, so advancing looper (with runToEndOfTasks())
+ // above does NOT advance the handlers' clock, hence whenever a handler post messages with
+ // specific time to the looper the time of those messages will be before the looper's time.
+ // To fix this we advance SystemClock as well since that is from where the handlers read
+ // time.
+ ShadowSystemClock.setCurrentTimeMillis(shadowBackupLooper.getScheduler().getCurrentTime());
+ return backupManagerService;
+ }
+
+ /** Sets up basic mocks for {@link BackupManagerService} mock. */
+ @SuppressWarnings("ResultOfMethodCallIgnored")
public static void setUpBackupManagerServiceBasics(
BackupManagerService backupManagerService,
Context context,
TransportManager transportManager,
PackageManager packageManager,
- BackupHandler backupHandler,
+ Handler backupHandler,
PowerManager.WakeLock wakeLock,
BackupAgentTimeoutParameters agentTimeoutParameters) {
+ SparseArray<Operation> operations = new SparseArray<>();
+
when(backupManagerService.getContext()).thenReturn(context);
when(backupManagerService.getTransportManager()).thenReturn(transportManager);
when(backupManagerService.getPackageManager()).thenReturn(packageManager);
when(backupManagerService.getBackupHandler()).thenReturn(backupHandler);
when(backupManagerService.getCurrentOpLock()).thenReturn(new Object());
when(backupManagerService.getQueueLock()).thenReturn(new Object());
- when(backupManagerService.getCurrentOperations()).thenReturn(new SparseArray<>());
+ when(backupManagerService.getCurrentOperations()).thenReturn(operations);
when(backupManagerService.getActivityManager()).thenReturn(mock(IActivityManager.class));
when(backupManagerService.getWakelock()).thenReturn(wakeLock);
when(backupManagerService.getAgentTimeoutParameters()).thenReturn(agentTimeoutParameters);
+
+ AccessorMock backupEnabled = mockAccessor(false);
+ doAnswer(backupEnabled.getter).when(backupManagerService).isBackupEnabled();
+ doAnswer(backupEnabled.setter).when(backupManagerService).setBackupEnabled(anyBoolean());
+
+ AccessorMock backupRunning = mockAccessor(false);
+ doAnswer(backupEnabled.getter).when(backupManagerService).isBackupRunning();
+ doAnswer(backupRunning.setter).when(backupManagerService).setBackupRunning(anyBoolean());
+
+ doAnswer(
+ invocation -> {
+ operations.put(invocation.getArgument(0), invocation.getArgument(1));
+ return null;
+ })
+ .when(backupManagerService)
+ .putOperation(anyInt(), any());
+ doAnswer(
+ invocation -> {
+ int token = invocation.getArgument(0);
+ operations.remove(token);
+ return null;
+ })
+ .when(backupManagerService)
+ .removeOperation(anyInt());
+ }
+
+ /**
+ * Returns one getter {@link Answer<T>} and one setter {@link Answer<T>} to be easily passed to
+ * Mockito mocking facilities.
+ *
+ * @param defaultValue Value returned by the getter if there was no setter call until then.
+ */
+ public static <T> AccessorMock<T> mockAccessor(T defaultValue) {
+ AtomicReference<T> holder = new AtomicReference<>(defaultValue);
+ return new AccessorMock<>(
+ invocation -> holder.get(),
+ invocation -> {
+ holder.set(invocation.getArgument(0));
+ return null;
+ });
}
public static PowerManager.WakeLock createBackupWakeLock(Application application) {
@@ -88,12 +181,38 @@
* @return The backup thread.
* @see #startBackupThreadAndGetLooper()
*/
- public static HandlerThread startBackupThread(UncaughtExceptionHandler exceptionHandler) {
+ public static HandlerThread startBackupThread(
+ @Nullable UncaughtExceptionHandler exceptionHandler) {
HandlerThread backupThread = new HandlerThread("backup");
backupThread.setUncaughtExceptionHandler(exceptionHandler);
backupThread.start();
return backupThread;
}
+ /**
+ * Similar to {@link #startBackupThread(UncaughtExceptionHandler)} but logging uncaught
+ * exceptions to logcat.
+ *
+ * @param tag Tag used for logging exceptions.
+ * @return The backup thread.
+ * @see #startBackupThread(UncaughtExceptionHandler)
+ */
+ public static HandlerThread startSilentBackupThread(String tag) {
+ return startBackupThread(
+ (thread, e) ->
+ ShadowLog.e(
+ tag, "Uncaught exception in test thread " + thread.getName(), e));
+ }
+
private BackupManagerServiceTestUtils() {}
+
+ public static class AccessorMock<T> {
+ public Answer<T> getter;
+ public Answer<T> setter;
+
+ private AccessorMock(Answer<T> getter, Answer<T> setter) {
+ this.getter = getter;
+ this.setter = setter;
+ }
+ }
}
diff --git a/services/robotests/src/com/android/server/backup/testing/TransportTestUtils.java b/services/robotests/src/com/android/server/backup/testing/TransportTestUtils.java
index 5844131..6625443 100644
--- a/services/robotests/src/com/android/server/backup/testing/TransportTestUtils.java
+++ b/services/robotests/src/com/android/server/backup/testing/TransportTestUtils.java
@@ -26,13 +26,13 @@
import static java.util.stream.Collectors.toList;
+import android.annotation.IntDef;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.os.RemoteException;
-import android.support.annotation.IntDef;
import com.android.internal.backup.IBackupTransport;
import com.android.server.backup.TransportManager;
@@ -42,6 +42,8 @@
import org.robolectric.shadows.ShadowPackageManager;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.List;
import java.util.stream.Stream;
@@ -209,6 +211,7 @@
TransportStatus.REGISTERED_UNAVAILABLE,
TransportStatus.UNREGISTERED
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface TransportStatus {
int REGISTERED_AVAILABLE = 0;
int REGISTERED_UNAVAILABLE = 1;
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java
index 424c08c..2214d74 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java
@@ -80,7 +80,7 @@
MockitoAnnotations.initMocks(this);
final Context context = InstrumentationRegistry.getTargetContext();
mUserId = ActivityManager.getCurrentUser();
- mCommand = new LockSettingsShellCommand(context, mLockPatternUtils);
+ mCommand = new LockSettingsShellCommand(mLockPatternUtils);
}
@Test
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 35f64a1..dd1ddfa 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -924,8 +924,7 @@
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
- final int packageUid = mPackageManagerInternal.getPackageUid(packageName,
- PackageManager.MATCH_ANY_USER, userId);
+ final int packageUid = mPackageManagerInternal.getPackageUid(packageName, 0, userId);
// If the calling app is asking about itself, continue, else check for permission.
if (packageUid != callingUid) {
if (!hasPermission(callingPackage)) {
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 4efe0b5..7d38e82 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -64,6 +64,9 @@
private String mLastBackgroundedPackage;
private final int mUserId;
+ // STOPSHIP: Temporary member variable for debugging b/110930764.
+ private UsageEvents.Event mLastEvent;
+
private static final long[] INTERVAL_LENGTH = new long[] {
UnixCalendar.DAY_IN_MILLIS, UnixCalendar.WEEK_IN_MILLIS,
UnixCalendar.MONTH_IN_MILLIS, UnixCalendar.YEAR_IN_MILLIS
@@ -156,6 +159,8 @@
+ eventToString(event.mEventType));
}
+ mLastEvent = new UsageEvents.Event(event);
+
if (event.mTimeStamp >= mDailyExpiryDate.getTimeInMillis()) {
// Need to rollover
rolloverStats(event.mTimeStamp);
@@ -306,6 +311,36 @@
Slog.d(TAG, mLogPrefix + "Requesting stats after " + beginTime + " but latest is "
+ currentStats.endTime);
}
+
+ // STOPSHIP: Temporary logging for b/110930764.
+ if (intervalType == UsageStatsManager.INTERVAL_DAILY
+ && mLastEvent.mTimeStamp >= beginTime) {
+ final IntervalStats diskStats = mDatabase.getLatestUsageStats(
+ UsageStatsManager.INTERVAL_DAILY);
+ StringBuilder sb = new StringBuilder(256);
+ sb.append("Last 24 hours of UsageStats missing! timeRange : ");
+ sb.append(beginTime);
+ sb.append(", ");
+ sb.append(endTime);
+ sb.append("\nLast reported Usage Event time : ");
+ sb.append(mLastEvent.mTimeStamp);
+ if (currentStats == null) {
+ sb.append("\nNo in memory event stats available.");
+ } else {
+ sb.append("\nLast in memory event time : ");
+ sb.append(currentStats.endTime);
+ sb.append("\nLast save time: ");
+ sb.append(currentStats.lastTimeSaved);
+ }
+ if (diskStats == null) {
+ sb.append("\nNo on disk event stats available.");
+ } else {
+ sb.append("\nLast on disk event time : ");
+ sb.append(diskStats.endTime);
+ }
+ Slog.wtf(TAG, sb.toString());
+ }
+
// Nothing newer available.
return null;
}
diff --git a/telephony/java/android/telephony/NetworkRegistrationState.java b/telephony/java/android/telephony/NetworkRegistrationState.java
index e881549..c393155 100644
--- a/telephony/java/android/telephony/NetworkRegistrationState.java
+++ b/telephony/java/android/telephony/NetworkRegistrationState.java
@@ -21,6 +21,7 @@
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
+import android.telephony.AccessNetworkConstants.TransportType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -59,15 +60,15 @@
/** Not registered. The device is not currently searching a new operator to register */
public static final int REG_STATE_NOT_REG_NOT_SEARCHING = 0;
/** Registered on home network */
- public static final int REG_STATE_HOME = 1;
+ public static final int REG_STATE_HOME = 1;
/** Not registered. The device is currently searching a new operator to register */
- public static final int REG_STATE_NOT_REG_SEARCHING = 2;
+ public static final int REG_STATE_NOT_REG_SEARCHING = 2;
/** Registration denied */
- public static final int REG_STATE_DENIED = 3;
+ public static final int REG_STATE_DENIED = 3;
/** Registration state is unknown */
- public static final int REG_STATE_UNKNOWN = 4;
+ public static final int REG_STATE_UNKNOWN = 4;
/** Registered on roaming network */
- public static final int REG_STATE_ROAMING = 5;
+ public static final int REG_STATE_ROAMING = 5;
/**
* Supported service type
@@ -79,16 +80,16 @@
SERVICE_TYPE_EMERGENCY})
public @interface ServiceType {}
- public static final int SERVICE_TYPE_VOICE = 1;
- public static final int SERVICE_TYPE_DATA = 2;
- public static final int SERVICE_TYPE_SMS = 3;
- public static final int SERVICE_TYPE_VIDEO = 4;
- public static final int SERVICE_TYPE_EMERGENCY = 5;
+ public static final int SERVICE_TYPE_VOICE = 1;
+ public static final int SERVICE_TYPE_DATA = 2;
+ public static final int SERVICE_TYPE_SMS = 3;
+ public static final int SERVICE_TYPE_VIDEO = 4;
+ public static final int SERVICE_TYPE_EMERGENCY = 5;
@Domain
private final int mDomain;
- /** {@link AccessNetworkConstants.TransportType}*/
+ /** {@link TransportType} */
private final int mTransportType;
@RegState
@@ -96,7 +97,7 @@
private final int mAccessNetworkTechnology;
- private final int mReasonForDenial;
+ private final int mRejectCause;
private final boolean mEmergencyOnly;
@@ -112,22 +113,35 @@
private DataSpecificRegistrationStates mDataSpecificStates;
/**
- * @param domain Network domain. Must be DOMAIN_CS or DOMAIN_PS.
- * @param transportType Transport type. Must be {@link AccessNetworkConstants.TransportType}
- * @param regState Network registration state.
- * @param accessNetworkTechnology See TelephonyManager NETWORK_TYPE_XXXX.
- * @param reasonForDenial Reason for denial if the registration state is DENIED.
- * @param availableServices The supported service.
- * @param cellIdentity The identity representing a unique cell
+ * @param domain Network domain. Must be a {@link Domain}. For {@link TransportType#WLAN}
+ * transport, this must set to {@link #DOMAIN_PS}.
+ * @param transportType Transport type. Must be one of the{@link TransportType}.
+ * @param regState Network registration state. Must be one of the {@link RegState}. For
+ * {@link TransportType#WLAN} transport, only {@link #REG_STATE_HOME} and
+ * {@link #REG_STATE_NOT_REG_NOT_SEARCHING} are valid states.
+ * @param accessNetworkTechnology Access network technology. Must be one of TelephonyManager
+ * NETWORK_TYPE_XXXX. For {@link TransportType#WLAN} transport, set to
+ * {@link TelephonyManager#NETWORK_TYPE_IWLAN}.
+ * @param rejectCause Reason for denial if the registration state is {@link #REG_STATE_DENIED}.
+ * Depending on {@code accessNetworkTechnology}, the values are defined in 3GPP TS 24.008
+ * 10.5.3.6 for UMTS, 3GPP TS 24.301 9.9.3.9 for LTE, and 3GPP2 A.S0001 6.2.2.44 for CDMA. If
+ * the reject cause is not supported or unknown, set it to 0.
+ * // TODO: Add IWLAN reject cause reference
+ * @param emergencyOnly True if this registration is for emergency only.
+ * @param availableServices The list of the supported services. Each element must be one of
+ * the {@link ServiceType}.
+ * @param cellIdentity The identity representing a unique cell or wifi AP. Set to null if the
+ * information is not available.
*/
- public NetworkRegistrationState(int domain, int transportType, int regState,
- int accessNetworkTechnology, int reasonForDenial, boolean emergencyOnly,
- int[] availableServices, @Nullable CellIdentity cellIdentity) {
+ public NetworkRegistrationState(@Domain int domain, int transportType, @RegState int regState,
+ int accessNetworkTechnology, int rejectCause,
+ boolean emergencyOnly, int[] availableServices,
+ @Nullable CellIdentity cellIdentity) {
mDomain = domain;
mTransportType = transportType;
mRegState = regState;
mAccessNetworkTechnology = accessNetworkTechnology;
- mReasonForDenial = reasonForDenial;
+ mRejectCause = rejectCause;
mAvailableServices = availableServices;
mCellIdentity = cellIdentity;
mEmergencyOnly = emergencyOnly;
@@ -138,11 +152,13 @@
* @hide
*/
public NetworkRegistrationState(int domain, int transportType, int regState,
- int accessNetworkTechnology, int reasonForDenial, boolean emergencyOnly,
- int[] availableServices, @Nullable CellIdentity cellIdentity, boolean cssSupported,
- int roamingIndicator, int systemIsInPrl, int defaultRoamingIndicator) {
- this(domain, transportType, regState, accessNetworkTechnology,
- reasonForDenial, emergencyOnly, availableServices, cellIdentity);
+ int accessNetworkTechnology, int rejectCause,
+ boolean emergencyOnly, int[] availableServices,
+ @Nullable CellIdentity cellIdentity, boolean cssSupported,
+ int roamingIndicator, int systemIsInPrl,
+ int defaultRoamingIndicator) {
+ this(domain, transportType, regState, accessNetworkTechnology, rejectCause, emergencyOnly,
+ availableServices, cellIdentity);
mVoiceSpecificStates = new VoiceSpecificRegistrationStates(cssSupported, roamingIndicator,
systemIsInPrl, defaultRoamingIndicator);
@@ -153,10 +169,11 @@
* @hide
*/
public NetworkRegistrationState(int domain, int transportType, int regState,
- int accessNetworkTechnology, int reasonForDenial, boolean emergencyOnly,
- int[] availableServices, @Nullable CellIdentity cellIdentity, int maxDataCalls) {
- this(domain, transportType, regState, accessNetworkTechnology,
- reasonForDenial, emergencyOnly, availableServices, cellIdentity);
+ int accessNetworkTechnology, int rejectCause,
+ boolean emergencyOnly, int[] availableServices,
+ @Nullable CellIdentity cellIdentity, int maxDataCalls) {
+ this(domain, transportType, regState, accessNetworkTechnology, rejectCause, emergencyOnly,
+ availableServices, cellIdentity);
mDataSpecificStates = new DataSpecificRegistrationStates(maxDataCalls);
}
@@ -166,7 +183,7 @@
mTransportType = source.readInt();
mRegState = source.readInt();
mAccessNetworkTechnology = source.readInt();
- mReasonForDenial = source.readInt();
+ mRejectCause = source.readInt();
mEmergencyOnly = source.readBoolean();
mAvailableServices = source.createIntArray();
mCellIdentity = source.readParcelable(CellIdentity.class.getClassLoader());
@@ -211,10 +228,10 @@
}
/**
- * @return Reason for denial from network.
+ * @return Network reject cause
*/
- public int getReasonForDenial() {
- return mReasonForDenial;
+ public int getRejectCause() {
+ return mRejectCause;
}
/**
@@ -265,7 +282,7 @@
.append(" regState=").append(regStateToString(mRegState))
.append(" accessNetworkTechnology=")
.append(TelephonyManager.getNetworkTypeName(mAccessNetworkTechnology))
- .append(" reasonForDenial=").append(mReasonForDenial)
+ .append(" rejectCause=").append(mRejectCause)
.append(" emergencyEnabled=").append(mEmergencyOnly)
.append(" supportedServices=").append(mAvailableServices)
.append(" cellIdentity=").append(mCellIdentity)
@@ -276,8 +293,8 @@
@Override
public int hashCode() {
- return Objects.hash(mTransportType, mDomain, mRegState, mAccessNetworkTechnology,
- mReasonForDenial, mEmergencyOnly, mAvailableServices, mCellIdentity,
+ return Objects.hash(mDomain, mTransportType, mRegState, mAccessNetworkTechnology,
+ mRejectCause, mEmergencyOnly, mAvailableServices, mCellIdentity,
mVoiceSpecificStates, mDataSpecificStates);
}
@@ -294,7 +311,7 @@
&& mTransportType == other.mTransportType
&& mRegState == other.mRegState
&& mAccessNetworkTechnology == other.mAccessNetworkTechnology
- && mReasonForDenial == other.mReasonForDenial
+ && mRejectCause == other.mRejectCause
&& mEmergencyOnly == other.mEmergencyOnly
&& (mAvailableServices == other.mAvailableServices
|| Arrays.equals(mAvailableServices, other.mAvailableServices))
@@ -309,7 +326,7 @@
dest.writeInt(mTransportType);
dest.writeInt(mRegState);
dest.writeInt(mAccessNetworkTechnology);
- dest.writeInt(mReasonForDenial);
+ dest.writeInt(mRejectCause);
dest.writeBoolean(mEmergencyOnly);
dest.writeIntArray(mAvailableServices);
dest.writeParcelable(mCellIdentity, 0);
diff --git a/tests/net/java/com/android/server/connectivity/tethering/SimChangeListenerTest.java b/tests/net/java/com/android/server/connectivity/tethering/SimChangeListenerTest.java
deleted file mode 100644
index f58ea7e..0000000
--- a/tests/net/java/com/android/server/connectivity/tethering/SimChangeListenerTest.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2017 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.connectivity.tethering;
-
-import static com.android.internal.telephony.IccCardConstants.INTENT_VALUE_ICC_ABSENT;
-import static com.android.internal.telephony.IccCardConstants.INTENT_VALUE_ICC_LOADED;
-import static com.android.internal.telephony.IccCardConstants.INTENT_KEY_ICC_STATE;
-import static com.android.internal.telephony.TelephonyIntents.ACTION_SIM_STATE_CHANGED;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.reset;
-
-import android.content.Context;
-import android.content.Intent;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.UserHandle;
-
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-
-import com.android.internal.util.test.BroadcastInterceptingContext;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.runner.RunWith;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class SimChangeListenerTest {
- @Mock private Context mContext;
- private BroadcastInterceptingContext mServiceContext;
- private Handler mHandler;
- private SimChangeListener mSCL;
- private int mCallbackCount;
-
- private void doCallback() { mCallbackCount++; }
-
- private class MockContext extends BroadcastInterceptingContext {
- MockContext(Context base) {
- super(base);
- }
- }
-
- @BeforeClass
- public static void setUpBeforeClass() throws Exception {
- if (Looper.myLooper() == null) {
- Looper.prepare();
- }
- }
-
- @Before public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
- reset(mContext);
- mServiceContext = new MockContext(mContext);
- mHandler = new Handler(Looper.myLooper());
- mCallbackCount = 0;
- mSCL = new SimChangeListener(mServiceContext, mHandler, () -> doCallback());
- }
-
- @After public void tearDown() throws Exception {
- if (mSCL != null) {
- mSCL.stopListening();
- mSCL = null;
- }
- }
-
- private void sendSimStateChangeIntent(String state) {
- final Intent intent = new Intent(ACTION_SIM_STATE_CHANGED);
- intent.putExtra(INTENT_KEY_ICC_STATE, state);
- mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
- }
-
- @Test
- public void testNotSeenFollowedBySeenCallsCallback() {
- mSCL.startListening();
-
- sendSimStateChangeIntent(INTENT_VALUE_ICC_ABSENT);
- sendSimStateChangeIntent(INTENT_VALUE_ICC_LOADED);
- assertEquals(1, mCallbackCount);
-
- sendSimStateChangeIntent(INTENT_VALUE_ICC_ABSENT);
- sendSimStateChangeIntent(INTENT_VALUE_ICC_LOADED);
- assertEquals(2, mCallbackCount);
-
- mSCL.stopListening();
- }
-
- @Test
- public void testNotListeningDoesNotCallback() {
- sendSimStateChangeIntent(INTENT_VALUE_ICC_ABSENT);
- sendSimStateChangeIntent(INTENT_VALUE_ICC_LOADED);
- assertEquals(0, mCallbackCount);
-
- sendSimStateChangeIntent(INTENT_VALUE_ICC_ABSENT);
- sendSimStateChangeIntent(INTENT_VALUE_ICC_LOADED);
- assertEquals(0, mCallbackCount);
- }
-
- @Test
- public void testSeenOnlyDoesNotCallback() {
- mSCL.startListening();
-
- sendSimStateChangeIntent(INTENT_VALUE_ICC_LOADED);
- assertEquals(0, mCallbackCount);
-
- sendSimStateChangeIntent(INTENT_VALUE_ICC_LOADED);
- assertEquals(0, mCallbackCount);
-
- mSCL.stopListening();
- }
-}