Merge "Keep track of primary dexopt"
diff --git a/api/current.txt b/api/current.txt
index d391076..0d422c5 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -388,6 +388,7 @@
field public static final int childIndicatorRight = 16843024; // 0x1010110
field public static final int childIndicatorStart = 16843731; // 0x10103d3
field public static final int choiceMode = 16843051; // 0x101012b
+ field public static final int classLoader = 16844139; // 0x101056b
field public static final int clearTaskOnLaunch = 16842773; // 0x1010015
field public static final int clickable = 16842981; // 0x10100e5
field public static final int clipChildren = 16842986; // 0x10100ea
@@ -38338,6 +38339,7 @@
field public static final int FD_CLOEXEC;
field public static final int FIONREAD;
field public static final int F_DUPFD;
+ field public static final int F_DUPFD_CLOEXEC;
field public static final int F_GETFD;
field public static final int F_GETFL;
field public static final int F_GETLK;
@@ -38432,7 +38434,9 @@
field public static final int NI_NUMERICSERV;
field public static final int O_ACCMODE;
field public static final int O_APPEND;
+ field public static final int O_CLOEXEC;
field public static final int O_CREAT;
+ field public static final int O_DSYNC;
field public static final int O_EXCL;
field public static final int O_NOCTTY;
field public static final int O_NOFOLLOW;
diff --git a/api/removed.txt b/api/removed.txt
index 49b72e15..3968fd3 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -15,6 +15,7 @@
public class Notification implements android.os.Parcelable {
method public deprecated java.lang.String getChannel();
+ method public static java.lang.Class<? extends android.app.Notification.Style> getNotificationStyleClass(java.lang.String);
method public deprecated long getTimeout();
method public deprecated void setLatestEventInfo(android.content.Context, java.lang.CharSequence, java.lang.CharSequence, android.app.PendingIntent);
}
@@ -451,6 +452,26 @@
}
+package android.service.notification {
+
+ public abstract class NotificationListenerService extends android.app.Service {
+ method public android.service.notification.StatusBarNotification[] getActiveNotifications(int);
+ method public android.service.notification.StatusBarNotification[] getActiveNotifications(java.lang.String[], int);
+ method public void registerAsSystemService(android.content.Context, android.content.ComponentName, int) throws android.os.RemoteException;
+ method public final void setOnNotificationPostedTrim(int);
+ method public final void snoozeNotification(java.lang.String, java.lang.String);
+ method public void unregisterAsSystemService() throws android.os.RemoteException;
+ field public static final int TRIM_FULL = 0; // 0x0
+ field public static final int TRIM_LIGHT = 1; // 0x1
+ }
+
+ public static class NotificationListenerService.Ranking {
+ method public java.util.List<java.lang.String> getAdditionalPeople();
+ method public java.util.List<android.service.notification.SnoozeCriterion> getSnoozeCriteria();
+ }
+
+}
+
package android.speech.tts {
public abstract class UtteranceProgressListener {
diff --git a/api/system-current.txt b/api/system-current.txt
index a2bb218..4cae69c 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -511,6 +511,7 @@
field public static final int childIndicatorRight = 16843024; // 0x1010110
field public static final int childIndicatorStart = 16843731; // 0x10103d3
field public static final int choiceMode = 16843051; // 0x101012b
+ field public static final int classLoader = 16844139; // 0x101056b
field public static final int clearTaskOnLaunch = 16842773; // 0x1010015
field public static final int clickable = 16842981; // 0x10100e5
field public static final int clipChildren = 16842986; // 0x10100ea
@@ -41562,6 +41563,7 @@
field public static final int FD_CLOEXEC;
field public static final int FIONREAD;
field public static final int F_DUPFD;
+ field public static final int F_DUPFD_CLOEXEC;
field public static final int F_GETFD;
field public static final int F_GETFL;
field public static final int F_GETLK;
@@ -41656,7 +41658,9 @@
field public static final int NI_NUMERICSERV;
field public static final int O_ACCMODE;
field public static final int O_APPEND;
+ field public static final int O_CLOEXEC;
field public static final int O_CREAT;
+ field public static final int O_DSYNC;
field public static final int O_EXCL;
field public static final int O_NOCTTY;
field public static final int O_NOFOLLOW;
diff --git a/api/test-current.txt b/api/test-current.txt
index deb3258..5d2c557 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -388,6 +388,7 @@
field public static final int childIndicatorRight = 16843024; // 0x1010110
field public static final int childIndicatorStart = 16843731; // 0x10103d3
field public static final int choiceMode = 16843051; // 0x101012b
+ field public static final int classLoader = 16844139; // 0x101056b
field public static final int clearTaskOnLaunch = 16842773; // 0x1010015
field public static final int clickable = 16842981; // 0x10100e5
field public static final int clipChildren = 16842986; // 0x10100ea
@@ -37602,7 +37603,6 @@
method public static void requestRebind(android.content.ComponentName);
method public final void requestUnbind();
method public final void setNotificationsShown(java.lang.String[]);
- method public final void snoozeNotification(java.lang.String, java.lang.String);
method public final void snoozeNotification(java.lang.String, long);
method public final void updateNotificationChannel(java.lang.String, android.os.UserHandle, android.app.NotificationChannel);
field public static final int HINT_HOST_DISABLE_CALL_EFFECTS = 4; // 0x4
@@ -37643,14 +37643,12 @@
public static class NotificationListenerService.Ranking {
ctor public NotificationListenerService.Ranking();
method public boolean canShowBadge();
- method public java.util.List<java.lang.String> getAdditionalPeople();
method public android.app.NotificationChannel getChannel();
method public int getImportance();
method public java.lang.CharSequence getImportanceExplanation();
method public java.lang.String getKey();
method public java.lang.String getOverrideGroupKey();
method public int getRank();
- method public java.util.List<android.service.notification.SnoozeCriterion> getSnoozeCriteria();
method public int getSuppressedVisualEffects();
method public boolean isAmbient();
method public boolean matchesInterruptionFilter();
@@ -38552,6 +38550,7 @@
field public static final int FD_CLOEXEC;
field public static final int FIONREAD;
field public static final int F_DUPFD;
+ field public static final int F_DUPFD_CLOEXEC;
field public static final int F_GETFD;
field public static final int F_GETFL;
field public static final int F_GETLK;
@@ -38646,7 +38645,9 @@
field public static final int NI_NUMERICSERV;
field public static final int O_ACCMODE;
field public static final int O_APPEND;
+ field public static final int O_CLOEXEC;
field public static final int O_CREAT;
+ field public static final int O_DSYNC;
field public static final int O_EXCL;
field public static final int O_NOCTTY;
field public static final int O_NOFOLLOW;
diff --git a/api/test-removed.txt b/api/test-removed.txt
index 49b72e15..3968fd3 100644
--- a/api/test-removed.txt
+++ b/api/test-removed.txt
@@ -15,6 +15,7 @@
public class Notification implements android.os.Parcelable {
method public deprecated java.lang.String getChannel();
+ method public static java.lang.Class<? extends android.app.Notification.Style> getNotificationStyleClass(java.lang.String);
method public deprecated long getTimeout();
method public deprecated void setLatestEventInfo(android.content.Context, java.lang.CharSequence, java.lang.CharSequence, android.app.PendingIntent);
}
@@ -451,6 +452,26 @@
}
+package android.service.notification {
+
+ public abstract class NotificationListenerService extends android.app.Service {
+ method public android.service.notification.StatusBarNotification[] getActiveNotifications(int);
+ method public android.service.notification.StatusBarNotification[] getActiveNotifications(java.lang.String[], int);
+ method public void registerAsSystemService(android.content.Context, android.content.ComponentName, int) throws android.os.RemoteException;
+ method public final void setOnNotificationPostedTrim(int);
+ method public final void snoozeNotification(java.lang.String, java.lang.String);
+ method public void unregisterAsSystemService() throws android.os.RemoteException;
+ field public static final int TRIM_FULL = 0; // 0x0
+ field public static final int TRIM_LIGHT = 1; // 0x1
+ }
+
+ public static class NotificationListenerService.Ranking {
+ method public java.util.List<java.lang.String> getAdditionalPeople();
+ method public java.util.List<android.service.notification.SnoozeCriterion> getSnoozeCriteria();
+ }
+
+}
+
package android.speech.tts {
public abstract class UtteranceProgressListener {
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index ad989de..f0189c2 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -414,7 +414,7 @@
try {
ApkLite baseApk = PackageParser.parseApkLite(file, 0);
PackageLite pkgLite = new PackageLite(null, baseApk, null, null, null, null,
- null, null);
+ null, null, null);
params.sessionParams.setSize(
PackageHelper.calculateInstalledSize(pkgLite, false,
params.sessionParams.abiOverride));
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 1c1d2ce..c994e13 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -658,6 +658,7 @@
}
static final class DumpHeapData {
+ public boolean runGc;
String path;
ParcelFileDescriptor fd;
}
@@ -1023,8 +1024,10 @@
sendMessage(H.PROFILER_CONTROL, profilerInfo, start ? 1 : 0, profileType);
}
- public void dumpHeap(boolean managed, String path, ParcelFileDescriptor fd) {
+ @Override
+ public void dumpHeap(boolean managed, boolean runGc, String path, ParcelFileDescriptor fd) {
DumpHeapData dhd = new DumpHeapData();
+ dhd.runGc = runGc;
dhd.path = path;
dhd.fd = fd;
sendMessage(H.DUMP_HEAP, dhd, managed ? 1 : 0, 0, true /*async*/);
@@ -5171,6 +5174,11 @@
}
static final void handleDumpHeap(boolean managed, DumpHeapData dhd) {
+ if (dhd.runGc) {
+ System.gc();
+ System.runFinalization();
+ System.gc();
+ }
if (managed) {
try {
Debug.dumpHprofData(dhd.path, dhd.fd.getFileDescriptor());
diff --git a/core/java/android/app/ApplicationLoaders.java b/core/java/android/app/ApplicationLoaders.java
index 2062930..b7c1f4e 100644
--- a/core/java/android/app/ApplicationLoaders.java
+++ b/core/java/android/app/ApplicationLoaders.java
@@ -18,9 +18,8 @@
import android.os.Build;
import android.os.Trace;
-import android.text.TextUtils;
import android.util.ArrayMap;
-import com.android.internal.os.PathClassLoaderFactory;
+import com.android.internal.os.ClassLoaderFactory;
import dalvik.system.PathClassLoader;
/** @hide */
@@ -31,15 +30,16 @@
ClassLoader getClassLoader(String zip, int targetSdkVersion, boolean isBundled,
String librarySearchPath, String libraryPermittedPath,
- ClassLoader parent) {
+ ClassLoader parent, String classLoaderName) {
// For normal usage the cache key used is the same as the zip path.
return getClassLoader(zip, targetSdkVersion, isBundled, librarySearchPath,
- libraryPermittedPath, parent, zip);
+ libraryPermittedPath, parent, zip, classLoaderName);
}
private ClassLoader getClassLoader(String zip, int targetSdkVersion, boolean isBundled,
String librarySearchPath, String libraryPermittedPath,
- ClassLoader parent, String cacheKey) {
+ ClassLoader parent, String cacheKey,
+ String classLoaderName) {
/*
* This is the parent we use if they pass "null" in. In theory
* this should be the "system" class loader; in practice we
@@ -66,28 +66,25 @@
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, zip);
- PathClassLoader pathClassloader = PathClassLoaderFactory.createClassLoader(
- zip,
- librarySearchPath,
- libraryPermittedPath,
- parent,
- targetSdkVersion,
- isBundled);
+ ClassLoader classloader = ClassLoaderFactory.createClassLoader(
+ zip, librarySearchPath, libraryPermittedPath, parent,
+ targetSdkVersion, isBundled, classLoaderName);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "setupVulkanLayerPath");
- setupVulkanLayerPath(pathClassloader, librarySearchPath);
+ setupVulkanLayerPath(classloader, librarySearchPath);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- mLoaders.put(cacheKey, pathClassloader);
- return pathClassloader;
+ mLoaders.put(cacheKey, classloader);
+ return classloader;
}
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, zip);
- PathClassLoader pathClassloader = new PathClassLoader(zip, parent);
+ ClassLoader loader = ClassLoaderFactory.createClassLoader(
+ zip, null, parent, classLoaderName);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- return pathClassloader;
+ return loader;
}
}
@@ -105,7 +102,7 @@
// The cache key is passed separately to enable the stub WebView to be cached under the
// stub's APK path, when the actual package path is the donor APK.
return getClassLoader(packagePath, Build.VERSION.SDK_INT, false, libsPath, null, null,
- cacheKey);
+ cacheKey, null /* classLoaderName */);
}
private static native void setupVulkanLayerPath(ClassLoader classLoader, String librarySearchPath);
@@ -122,7 +119,7 @@
baseDexClassLoader.addDexPath(dexPath);
}
- private final ArrayMap<String, ClassLoader> mLoaders = new ArrayMap<String, ClassLoader>();
+ private final ArrayMap<String, ClassLoader> mLoaders = new ArrayMap<>();
private static final ApplicationLoaders gApplicationLoaders = new ApplicationLoaders();
}
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index b27e224..3be6f97 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -277,7 +277,7 @@
int checkGrantUriPermission(int callingUid, in String targetPkg, in Uri uri,
int modeFlags, int userId);
// Cause the specified process to dump the specified heap.
- boolean dumpHeap(in String process, int userId, boolean managed, in String path,
+ boolean dumpHeap(in String process, int userId, boolean managed, boolean runGc, in String path,
in ParcelFileDescriptor fd);
int startActivities(in IApplicationThread caller, in String callingPackage,
in Intent[] intents, in String[] resolvedTypes, in IBinder resultTo,
diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl
index bd8c5c9..7191fc4 100644
--- a/core/java/android/app/IApplicationThread.aidl
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -117,7 +117,7 @@
void scheduleSuicide();
void dispatchPackageBroadcast(int cmd, in String[] packages);
void scheduleCrash(in String msg);
- void dumpHeap(boolean managed, in String path, in ParcelFileDescriptor fd);
+ void dumpHeap(boolean managed, boolean runGc, in String path, in ParcelFileDescriptor fd);
void dumpActivity(in ParcelFileDescriptor fd, IBinder servicetoken, in String prefix,
in String[] args);
void clearDnsCache();
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 79e5407..b38be66 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -97,7 +97,6 @@
private String mAppDir;
private String mResDir;
private String[] mOverlayDirs;
- private String[] mSharedLibraries;
private String mDataDir;
private String mLibDir;
private File mDataDirFile;
@@ -116,6 +115,7 @@
private String[] mSplitNames;
private String[] mSplitAppDirs;
private String[] mSplitResDirs;
+ private String[] mSplitClassLoaderNames;
private final ArrayMap<Context, ArrayMap<BroadcastReceiver, ReceiverDispatcher>> mReceivers
= new ArrayMap<>();
@@ -126,8 +126,6 @@
private final ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>> mUnboundServices
= new ArrayMap<>();
- int mClientCount = 0;
-
Application getApplication() {
return mApplication;
}
@@ -192,8 +190,8 @@
mResDir = null;
mSplitAppDirs = null;
mSplitResDirs = null;
+ mSplitClassLoaderNames = null;
mOverlayDirs = null;
- mSharedLibraries = null;
mDataDir = null;
mDataDirFile = null;
mDeviceProtectedDataDirFile = null;
@@ -324,7 +322,6 @@
mAppDir = aInfo.sourceDir;
mResDir = aInfo.uid == myUid ? aInfo.sourceDir : aInfo.publicSourceDir;
mOverlayDirs = aInfo.resourceDirs;
- mSharedLibraries = aInfo.sharedLibraryFiles;
mDataDir = aInfo.dataDir;
mLibDir = aInfo.nativeLibraryDir;
mDataDirFile = FileUtils.newFileOrNull(aInfo.dataDir);
@@ -334,6 +331,7 @@
mSplitNames = aInfo.splitNames;
mSplitAppDirs = aInfo.splitSourceDirs;
mSplitResDirs = aInfo.uid == myUid ? aInfo.splitSourceDirs : aInfo.splitPublicSourceDirs;
+ mSplitClassLoaderNames = aInfo.splitClassLoaderNames;
if (aInfo.requestsIsolatedSplitLoading() && !ArrayUtils.isEmpty(mSplitNames)) {
mSplitLoader = new SplitDependencyLoaderImpl(aInfo.splitDependencies);
@@ -530,7 +528,8 @@
// Since we handled the special base case above, parentSplitIdx is always valid.
final ClassLoader parent = mCachedClassLoaders[parentSplitIdx];
mCachedClassLoaders[splitIdx] = ApplicationLoaders.getDefault().getClassLoader(
- mSplitAppDirs[splitIdx - 1], getTargetSdkVersion(), false, null, null, parent);
+ mSplitAppDirs[splitIdx - 1], getTargetSdkVersion(), false, null, null, parent,
+ mSplitClassLoaderNames[splitIdx - 1]);
Collections.addAll(splitPaths, mCachedResourcePaths[parentSplitIdx]);
splitPaths.add(mSplitResDirs[splitIdx - 1]);
@@ -650,8 +649,9 @@
if (mClassLoader == null) {
StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
mClassLoader = ApplicationLoaders.getDefault().getClassLoader(
- "" /* codePath */, mApplicationInfo.targetSdkVersion, isBundledApp,
- librarySearchPath, libraryPermittedPath, mBaseClassLoader);
+ "" /* codePath */, mApplicationInfo.targetSdkVersion, isBundledApp,
+ librarySearchPath, libraryPermittedPath, mBaseClassLoader,
+ null /* classLoaderName */);
StrictMode.setThreadPolicy(oldPolicy);
}
@@ -678,7 +678,8 @@
mClassLoader = ApplicationLoaders.getDefault().getClassLoader(zip,
mApplicationInfo.targetSdkVersion, isBundledApp, librarySearchPath,
- libraryPermittedPath, mBaseClassLoader);
+ libraryPermittedPath, mBaseClassLoader,
+ mApplicationInfo.classLoaderName);
StrictMode.setThreadPolicy(oldPolicy);
// Setup the class loader paths for profiling.
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 5a08cf3..a3377d7 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -561,6 +561,11 @@
@SystemApi
public static final int FLAG_AUTOGROUP_SUMMARY = 0x00000400;
+ /**
+ * @hide
+ */
+ public static final int FLAG_CAN_COLORIZE = 0x00000800;
+
public int flags;
/** @hide */
@@ -2538,6 +2543,22 @@
}
}
+ /**
+ * @hide
+ */
+ public boolean hasCompletedProgress() {
+ // not a progress notification; can't be complete
+ if (!extras.containsKey(EXTRA_PROGRESS)
+ || !extras.containsKey(EXTRA_PROGRESS_MAX)) {
+ return false;
+ }
+ // many apps use max 0 for 'indeterminate'; not complete
+ if (extras.getInt(EXTRA_PROGRESS_MAX) == 0) {
+ return false;
+ }
+ return extras.getInt(EXTRA_PROGRESS) == extras.getInt(EXTRA_PROGRESS_MAX);
+ }
+
/** @removed */
@Deprecated
public String getChannel() {
@@ -5174,7 +5195,16 @@
if (isColorizedMedia()) {
return true;
}
- return extras.getBoolean(EXTRA_COLORIZED) && isForegroundService();
+ return extras.getBoolean(EXTRA_COLORIZED)
+ && (hasColorizedPermission() || isForegroundService());
+ }
+
+ /**
+ * Returns whether an app can colorize due to the android.permission.USE_COLORIZED_NOTIFICATIONS
+ * permission. The permission is checked when a notification is enqueued.
+ */
+ private boolean hasColorizedPermission() {
+ return (flags & Notification.FLAG_CAN_COLORIZE) != 0;
}
/**
@@ -5234,7 +5264,7 @@
}
/**
- * @hide
+ * @removed
*/
@SystemApi
public static Class<? extends Style> getNotificationStyleClass(String templateClass) {
diff --git a/core/java/android/app/SharedPreferencesImpl.java b/core/java/android/app/SharedPreferencesImpl.java
index 063ad24..1758a06 100644
--- a/core/java/android/app/SharedPreferencesImpl.java
+++ b/core/java/android/app/SharedPreferencesImpl.java
@@ -778,7 +778,7 @@
}
long fsyncDuration = fsyncTime - writeTime;
- mSyncTimes.add(Long.valueOf(fsyncDuration).intValue());
+ mSyncTimes.add((int) fsyncDuration);
mNumSync++;
if (DEBUG || mNumSync % 1024 == 0 || fsyncDuration > MAX_FSYNC_DURATION_MILLIS) {
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index f18aa58..ab70f0e7 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -83,6 +83,8 @@
import android.net.NetworkScoreManager;
import android.net.nsd.INsdManager;
import android.net.nsd.NsdManager;
+import android.net.lowpan.ILowpanManager;
+import android.net.lowpan.LowpanManager;
import android.net.wifi.IRttManager;
import android.net.wifi.IWifiManager;
import android.net.wifi.IWifiScanner;
@@ -540,6 +542,16 @@
ctx.mMainThread.getHandler());
}});
+ registerService(Context.LOWPAN_SERVICE, LowpanManager.class,
+ new CachedServiceFetcher<LowpanManager>() {
+ @Override
+ public LowpanManager createService(ContextImpl ctx) throws ServiceNotFoundException {
+ IBinder b = ServiceManager.getServiceOrThrow(Context.LOWPAN_SERVICE);
+ ILowpanManager service = ILowpanManager.Stub.asInterface(b);
+ return new LowpanManager(ctx.getOuterContext(), service,
+ ConnectivityThread.getInstanceLooper());
+ }});
+
registerService(Context.WIFI_SERVICE, WifiManager.class,
new CachedServiceFetcher<WifiManager>() {
@Override
diff --git a/core/java/android/app/WallpaperColors.java b/core/java/android/app/WallpaperColors.java
index 57c7efc..b9d3e75 100644
--- a/core/java/android/app/WallpaperColors.java
+++ b/core/java/android/app/WallpaperColors.java
@@ -26,6 +26,7 @@
import android.os.Parcelable;
import android.util.Size;
+import com.android.internal.graphics.ColorUtils;
import com.android.internal.graphics.palette.Palette;
import com.android.internal.graphics.palette.VariationalKMeansQuantizer;
@@ -50,6 +51,14 @@
*/
public static final int HINT_SUPPORTS_DARK_TEXT = 0x1;
+ /**
+ * Specifies that dark theme is preferred over the current wallpaper for best presentation.
+ * <p>
+ * eg. A launcher may set its drawer color to black if this flag is specified.
+ * @hide
+ */
+ public static final int HINT_SUPPORTS_DARK_THEME = 0x2;
+
// Maximum size that a bitmap can have to keep our calculations sane
private static final int MAX_BITMAP_SIZE = 112;
@@ -61,8 +70,10 @@
// present in at least MIN_COLOR_OCCURRENCE of the image
private static final float MIN_COLOR_OCCURRENCE = 0.05f;
+ // Decides when dark theme is optimal for this wallpaper
+ private static final float DARK_THEME_MEAN_LUMINANCE = 0.25f;
// Minimum mean luminosity that an image needs to have to support dark text
- private static final float BRIGHT_IMAGE_MEAN_LUMINANCE = 0.9f;
+ private static final float BRIGHT_IMAGE_MEAN_LUMINANCE = 0.75f;
// We also check if the image has dark pixels in it,
// to avoid bright images with some dark spots.
private static final float DARK_PIXEL_LUMINANCE = 0.45f;
@@ -169,10 +180,7 @@
}
}
- int hints = 0;
- if (calculateDarkTextSupport(bitmap)) {
- hints |= HINT_SUPPORTS_DARK_TEXT;
- }
+ int hints = calculateHints(bitmap);
return new WallpaperColors(primary, secondary, tertiary, hints);
}
@@ -335,9 +343,9 @@
* @param source What to read.
* @return Whether image supports dark text or not.
*/
- private static boolean calculateDarkTextSupport(Bitmap source) {
+ private static int calculateHints(Bitmap source) {
if (source == null) {
- return false;
+ return 0;
}
int[] pixels = new int[source.getWidth() * source.getHeight()];
@@ -349,22 +357,29 @@
// This bitmap was already resized to fit the maximum allowed area.
// Let's just loop through the pixels, no sweat!
+ float[] tmpHsl = new float[3];
for (int i = 0; i < pixels.length; i++) {
- final float luminance = Color.luminance(pixels[i]);
+ ColorUtils.colorToHSL(pixels[i], tmpHsl);
+ final float luminance = tmpHsl[2];
final int alpha = Color.alpha(pixels[i]);
-
// Make sure we don't have a dark pixel mass that will
// make text illegible.
if (luminance < DARK_PIXEL_LUMINANCE && alpha != 0) {
darkPixels++;
- if (darkPixels > maxDarkPixels) {
- return false;
- }
}
-
totalLuminance += luminance;
}
- return totalLuminance / pixels.length > BRIGHT_IMAGE_MEAN_LUMINANCE;
+
+ int hints = 0;
+ double meanLuminance = totalLuminance / pixels.length;
+ if (meanLuminance > BRIGHT_IMAGE_MEAN_LUMINANCE && darkPixels < maxDarkPixels) {
+ hints |= HINT_SUPPORTS_DARK_TEXT;
+ }
+ if (meanLuminance < DARK_THEME_MEAN_LUMINANCE) {
+ hints |= HINT_SUPPORTS_DARK_THEME;
+ }
+
+ return hints;
}
private static Size calculateOptimalSize(int width, int height) {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 128d195..63a2cf0 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2900,6 +2900,7 @@
WIFI_AWARE_SERVICE,
WIFI_P2P_SERVICE,
WIFI_SCANNING_SERVICE,
+ //@hide: LOWPAN_SERVICE,
//@hide: WIFI_RTT_SERVICE,
//@hide: ETHERNET_SERVICE,
WIFI_RTT_SERVICE,
@@ -2955,7 +2956,8 @@
SHORTCUT_SERVICE,
//@hide: CONTEXTHUB_SERVICE,
SYSTEM_HEALTH_SERVICE,
- //@hide: INCIDENT_SERVICE
+ //@hide: INCIDENT_SERVICE,
+ COMPANION_DEVICE_SERVICE
})
@Retention(RetentionPolicy.SOURCE)
public @interface ServiceName {}
@@ -3428,6 +3430,18 @@
/**
* Use with {@link #getSystemService} to retrieve a {@link
+ * android.net.lowpan.LowpanManager} for handling management of
+ * LoWPAN access.
+ *
+ * @see #getSystemService
+ * @see android.net.lowpan.LowpanManager
+ *
+ * @hide
+ */
+ public static final String LOWPAN_SERVICE = "lowpan";
+
+ /**
+ * Use with {@link #getSystemService} to retrieve a {@link
* android.net.EthernetManager} for handling management of
* Ethernet access.
*
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 06f7916..0bfe567 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -1005,6 +1005,12 @@
}
}
+ /** @hide */
+ public String classLoaderName;
+
+ /** @hide */
+ public String[] splitClassLoaderNames;
+
public void dump(Printer pw, String prefix) {
dump(pw, prefix, DUMP_FLAG_ALL);
}
@@ -1056,6 +1062,13 @@
pw.println(prefix + "sharedLibraryFiles=" + Arrays.toString(sharedLibraryFiles));
}
}
+ if (classLoaderName != null) {
+ pw.println(prefix + "classLoaderName=" + classLoaderName);
+ }
+ if (!ArrayUtils.isEmpty(splitClassLoaderNames)) {
+ pw.println(prefix + "splitClassLoaderNames=" + Arrays.toString(splitClassLoaderNames));
+ }
+
pw.println(prefix + "enabled=" + enabled
+ " minSdkVersion=" + minSdkVersion
+ " targetSdkVersion=" + targetSdkVersion
@@ -1178,6 +1191,8 @@
networkSecurityConfigRes = orig.networkSecurityConfigRes;
category = orig.category;
targetSandboxVersion = orig.targetSandboxVersion;
+ classLoaderName = orig.classLoaderName;
+ splitClassLoaderNames = orig.splitClassLoaderNames;
}
public String toString() {
@@ -1246,6 +1261,8 @@
dest.writeInt(networkSecurityConfigRes);
dest.writeInt(category);
dest.writeInt(targetSandboxVersion);
+ dest.writeString(classLoaderName);
+ dest.writeStringArray(splitClassLoaderNames);
}
public static final Parcelable.Creator<ApplicationInfo> CREATOR
@@ -1311,6 +1328,8 @@
networkSecurityConfigRes = source.readInt();
category = source.readInt();
targetSandboxVersion = source.readInt();
+ classLoaderName = source.readString();
+ splitClassLoaderNames = source.readStringArray();
}
/**
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index b8e751e..827711a 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2294,6 +2294,14 @@
/**
* Feature for {@link #getSystemAvailableFeatures} and
+ * {@link #hasSystemFeature}: The device supports LoWPAN networking.
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_LOWPAN = "android.hardware.lowpan";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: This is a device dedicated to showing UI
* on a vehicle headunit. A headunit here is defined to be inside a
* vehicle that may or may not be moving. A headunit uses either a
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index e4f2fc1..eb6e0d8 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -88,6 +88,7 @@
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.ClassLoaderFactory;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.XmlUtils;
@@ -415,9 +416,12 @@
public final boolean extractNativeLibs;
public final boolean isolatedSplits;
+ public final String classLoaderName;
+ public final String[] splitClassLoaderNames;
+
public PackageLite(String codePath, ApkLite baseApk, String[] splitNames,
boolean[] isFeatureSplits, String[] usesSplitNames, String[] configForSplit,
- String[] splitCodePaths, int[] splitRevisionCodes) {
+ String[] splitCodePaths, int[] splitRevisionCodes, String[] splitClassLoaderNames) {
this.packageName = baseApk.packageName;
this.versionCode = baseApk.versionCode;
this.installLocation = baseApk.installLocation;
@@ -437,6 +441,9 @@
this.use32bitAbi = baseApk.use32bitAbi;
this.extractNativeLibs = baseApk.extractNativeLibs;
this.isolatedSplits = baseApk.isolatedSplits;
+
+ this.classLoaderName = baseApk.classLoaderName;
+ this.splitClassLoaderNames = splitClassLoaderNames;
}
public List<String> getAllCodePaths() {
@@ -471,13 +478,14 @@
public final boolean use32bitAbi;
public final boolean extractNativeLibs;
public final boolean isolatedSplits;
+ public final String classLoaderName;
public ApkLite(String codePath, String packageName, String splitName, boolean isFeatureSplit,
String configForSplit, String usesSplitName, int versionCode, int revisionCode,
int installLocation, List<VerifierInfo> verifiers, Signature[] signatures,
Certificate[][] certificates, boolean coreApp, boolean debuggable,
boolean multiArch, boolean use32bitAbi, boolean extractNativeLibs,
- boolean isolatedSplits) {
+ boolean isolatedSplits, String classLoaderName) {
this.codePath = codePath;
this.packageName = packageName;
this.splitName = splitName;
@@ -496,6 +504,7 @@
this.use32bitAbi = use32bitAbi;
this.extractNativeLibs = extractNativeLibs;
this.isolatedSplits = isolatedSplits;
+ this.classLoaderName = classLoaderName;
}
}
@@ -863,7 +872,7 @@
final ApkLite baseApk = parseApkLite(packageFile, flags);
final String packagePath = packageFile.getAbsolutePath();
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- return new PackageLite(packagePath, baseApk, null, null, null, null, null, null);
+ return new PackageLite(packagePath, baseApk, null, null, null, null, null, null, null);
}
static PackageLite parseClusterPackageLite(File packageDir, int flags)
@@ -926,6 +935,7 @@
String[] configForSplits = null;
String[] splitCodePaths = null;
int[] splitRevisionCodes = null;
+ String[] splitClassLoaderNames = null;
if (size > 0) {
splitNames = new String[size];
isFeatureSplits = new boolean[size];
@@ -933,6 +943,7 @@
configForSplits = new String[size];
splitCodePaths = new String[size];
splitRevisionCodes = new int[size];
+ splitClassLoaderNames = new String[size];
splitNames = apks.keySet().toArray(splitNames);
Arrays.sort(splitNames, sSplitNameComparator);
@@ -944,12 +955,13 @@
configForSplits[i] = apk.configForSplit;
splitCodePaths[i] = apk.codePath;
splitRevisionCodes[i] = apk.revisionCode;
+ splitClassLoaderNames[i] = apk.classLoaderName;
}
}
final String codePath = packageDir.getAbsolutePath();
return new PackageLite(codePath, baseApk, splitNames, isFeatureSplits, usesSplitNames,
- configForSplits, splitCodePaths, splitRevisionCodes);
+ configForSplits, splitCodePaths, splitRevisionCodes, splitClassLoaderNames);
}
/**
@@ -1187,6 +1199,8 @@
pkg.splitPrivateFlags = new int[num];
pkg.applicationInfo.splitNames = pkg.splitNames;
pkg.applicationInfo.splitDependencies = splitDependencies;
+ pkg.applicationInfo.classLoaderName = lite.classLoaderName;
+ pkg.applicationInfo.splitClassLoaderNames = lite.splitClassLoaderNames;
for (int i = 0; i < num; i++) {
final AssetManager splitAssets = assetLoader.getSplitAssetManager(i);
@@ -1697,7 +1711,7 @@
}
final AttributeSet attrs = parser;
- return parseApkLite(apkPath, parser, attrs, flags, signatures, certificates);
+ return parseApkLite(apkPath, parser, attrs, signatures, certificates);
} catch (XmlPullParserException | IOException | RuntimeException e) {
Slog.w(TAG, "Failed to parse " + apkPath, e);
@@ -1784,7 +1798,7 @@
}
private static ApkLite parseApkLite(String codePath, XmlPullParser parser, AttributeSet attrs,
- int flags, Signature[] signatures, Certificate[][] certificates)
+ Signature[] signatures, Certificate[][] certificates)
throws IOException, XmlPullParserException, PackageParserException {
final Pair<String, String> packageSplit = parsePackageSplitNames(parser, attrs);
@@ -1800,6 +1814,7 @@
boolean isFeatureSplit = false;
String configForSplit = null;
String usesSplitName = null;
+ String classLoaderName = null;
for (int i = 0; i < attrs.getAttributeCount(); i++) {
final String attr = attrs.getAttributeName(i);
@@ -1856,6 +1871,14 @@
if ("extractNativeLibs".equals(attr)) {
extractNativeLibs = attrs.getAttributeBooleanValue(i, true);
}
+ if ("classLoader".equals(attr)) {
+ classLoaderName = attrs.getAttributeValue(i);
+ if (!ClassLoaderFactory.isValidClassLoaderName(classLoaderName)) {
+ throw new PackageParserException(
+ PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+ "Invalid class loader name: " + classLoaderName);
+ }
+ }
}
} else if (TAG_USES_SPLIT.equals(parser.getName())) {
if (usesSplitName != null) {
@@ -1875,19 +1898,7 @@
return new ApkLite(codePath, packageSplit.first, packageSplit.second, isFeatureSplit,
configForSplit, usesSplitName, versionCode, revisionCode, installLocation,
verifiers, signatures, certificates, coreApp, debuggable, multiArch, use32bitAbi,
- extractNativeLibs, isolatedSplits);
- }
-
- /**
- * Temporary.
- */
- static public Signature stringToSignature(String str) {
- final int N = str.length();
- byte[] sig = new byte[N];
- for (int i=0; i<N; i++) {
- sig[i] = (byte)str.charAt(i);
- }
- return new Signature(sig);
+ extractNativeLibs, isolatedSplits, classLoaderName);
}
/**
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 9b4ad73..ef279b8 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -1672,7 +1672,24 @@
n = this.densityDpi - that.densityDpi;
if (n != 0) return n;
n = this.assetsSeq - that.assetsSeq;
- //if (n != 0) return n;
+ if (n != 0) return n;
+
+ if (this.appBounds == null && that.appBounds != null) {
+ return 1;
+ } else if (this.appBounds != null && that.appBounds == null) {
+ return -1;
+ } else if (this.appBounds != null && that.appBounds != null) {
+ n = this.appBounds.left - that.appBounds.left;
+ if (n != 0) return n;
+ n = this.appBounds.top - that.appBounds.top;
+ if (n != 0) return n;
+ n = this.appBounds.right - that.appBounds.right;
+ if (n != 0) return n;
+ n = this.appBounds.bottom - that.appBounds.bottom;
+ if (n != 0) return n;
+ }
+
+ // if (n != 0) return n;
return n;
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 1ae9e15..6f54e36 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7191,6 +7191,10 @@
VR_DISPLAY_MODE,
NOTIFICATION_BADGING,
QS_AUTO_ADDED_TILES,
+ SCREENSAVER_ENABLED,
+ SCREENSAVER_COMPONENTS,
+ SCREENSAVER_ACTIVATE_ON_DOCK,
+ SCREENSAVER_ACTIVATE_ON_SLEEP,
};
/** @hide */
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 76c96bd..855c87b 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -16,35 +16,31 @@
package android.service.notification;
-import android.Manifest;
import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.annotation.TestApi;
-import android.app.NotificationChannel;
-import android.app.NotificationChannelGroup;
-import android.companion.CompanionDeviceManager;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-
-import android.annotation.SystemApi;
import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
import android.app.INotificationManager;
import android.app.Notification;
import android.app.Notification.Builder;
+import android.app.NotificationChannel;
+import android.app.NotificationChannelGroup;
import android.app.NotificationManager;
import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ParceledListSlice;
+import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
-import android.graphics.Bitmap;
import android.os.Build;
import android.os.Bundle;
+import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
@@ -54,6 +50,7 @@
import android.util.ArraySet;
import android.util.Log;
import android.widget.RemoteViews;
+
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.SomeArgs;
@@ -199,6 +196,7 @@
* The full trim of the StatusBarNotification including all its features.
*
* @hide
+ * @removed
*/
@SystemApi
public static final int TRIM_FULL = 0;
@@ -219,6 +217,7 @@
* </ol>
*
* @hide
+ * @removed
*/
@SystemApi
public static final int TRIM_LIGHT = 1;
@@ -605,9 +604,9 @@
* @param snoozeCriterionId The{@link SnoozeCriterion#getId()} of a context to snooze the
* notification until.
* @hide
+ * @removed
*/
@SystemApi
- @TestApi
public final void snoozeNotification(String key, String snoozeCriterionId) {
if (!isBound()) return;
try {
@@ -749,6 +748,7 @@
* before performing this operation.
*
* @hide
+ * @removed
*
* @param trim trim of the notifications to be passed via {@link #onNotificationPosted}.
* See <code>TRIM_*</code> constants.
@@ -801,6 +801,7 @@
* current user). Useful when you don't know what's already been posted.
*
* @hide
+ * @removed
*
* @param trim trim of the notifications to be returned. See <code>TRIM_*</code> constants.
* @return An array of active notifications, sorted in natural order.
@@ -832,6 +833,7 @@
* more data out of those notifications.
*
* @hide
+ * @removed
*
* @param keys the keys of the notifications to request
* @param trim trim of the notifications to be returned. See <code>TRIM_*</code> constants.
@@ -1046,6 +1048,7 @@
* @param componentName the component that will consume the notification information
* @param currentUser the user to use as the stream filter
* @hide
+ * @removed
*/
@SystemApi
public void registerAsSystemService(Context context, ComponentName componentName,
@@ -1066,6 +1069,7 @@
* <p>This method will fail for listeners that were not registered
* with (@link registerAsService).
* @hide
+ * @removed
*/
@SystemApi
public void unregisterAsSystemService() throws RemoteException {
@@ -1434,9 +1438,9 @@
* If the {@link NotificationAssistantService} has added people to this notification, then
* this will be non-null.
* @hide
+ * @removed
*/
@SystemApi
- @TestApi
public List<String> getAdditionalPeople() {
return mOverridePeople;
}
@@ -1446,9 +1450,9 @@
* user interface displays options for snoozing notifications these criteria should be
* displayed as well.
* @hide
+ * @removed
*/
@SystemApi
- @TestApi
public List<SnoozeCriterion> getSnoozeCriteria() {
return mSnoozeCriteria;
}
diff --git a/core/java/com/android/internal/os/ClassLoaderFactory.java b/core/java/com/android/internal/os/ClassLoaderFactory.java
new file mode 100644
index 0000000..0c041f2
--- /dev/null
+++ b/core/java/com/android/internal/os/ClassLoaderFactory.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2016 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.internal.os;
+
+import android.os.Trace;
+
+import dalvik.system.DelegateLastClassLoader;
+import dalvik.system.PathClassLoader;
+
+/**
+ * Creates class loaders.
+ *
+ * @hide
+ */
+public class ClassLoaderFactory {
+ // Unconstructable
+ private ClassLoaderFactory() {}
+
+ private static final String PATH_CLASS_LOADER_NAME = PathClassLoader.class.getName();
+ private static final String DELEGATE_LAST_CLASS_LOADER_NAME =
+ DelegateLastClassLoader.class.getName();
+
+ /**
+ * Returns true if {@code name} is a supported classloader. {@code name} must be a
+ * binary name of a class, as defined by {@code Class.getName}.
+ */
+ public static boolean isValidClassLoaderName(String name) {
+ return PATH_CLASS_LOADER_NAME.equals(name) ||
+ DELEGATE_LAST_CLASS_LOADER_NAME.equals(name);
+ }
+
+ /**
+ * Same as {@code createClassLoader} below, except that no associated namespace
+ * is created.
+ */
+ public static ClassLoader createClassLoader(String dexPath,
+ String librarySearchPath, ClassLoader parent, String classloaderName) {
+ if (classloaderName == null || PATH_CLASS_LOADER_NAME.equals(classloaderName)) {
+ return new PathClassLoader(dexPath, librarySearchPath, parent);
+ } else if (DELEGATE_LAST_CLASS_LOADER_NAME.equals(classloaderName)) {
+ return new DelegateLastClassLoader(dexPath, librarySearchPath, parent);
+ }
+
+ throw new AssertionError("Invalid classLoaderName: " + classloaderName);
+ }
+
+ /**
+ * Create a ClassLoader and initialize a linker-namespace for it.
+ */
+ public static ClassLoader createClassLoader(String dexPath,
+ String librarySearchPath, String libraryPermittedPath, ClassLoader parent,
+ int targetSdkVersion, boolean isNamespaceShared, String classloaderName) {
+
+ final ClassLoader classLoader = createClassLoader(dexPath, librarySearchPath, parent,
+ classloaderName);
+
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "createClassloaderNamespace");
+ String errorMessage = createClassloaderNamespace(classLoader,
+ targetSdkVersion,
+ librarySearchPath,
+ libraryPermittedPath,
+ isNamespaceShared);
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+
+ if (errorMessage != null) {
+ throw new UnsatisfiedLinkError("Unable to create namespace for the classloader " +
+ classLoader + ": " + errorMessage);
+ }
+
+ return classLoader;
+ }
+
+ private static native String createClassloaderNamespace(ClassLoader classLoader,
+ int targetSdkVersion,
+ String librarySearchPath,
+ String libraryPermittedPath,
+ boolean isNamespaceShared);
+}
diff --git a/core/java/com/android/internal/os/PathClassLoaderFactory.java b/core/java/com/android/internal/os/PathClassLoaderFactory.java
deleted file mode 100644
index 06a93b2..0000000
--- a/core/java/com/android/internal/os/PathClassLoaderFactory.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2016 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.internal.os;
-
-import android.os.Trace;
-
-import dalvik.system.PathClassLoader;
-
-/**
- * Creates path class loaders.
- *
- * @hide
- */
-public class PathClassLoaderFactory {
- // Unconstructable
- private PathClassLoaderFactory() {}
-
- /**
- * Create a PathClassLoader and initialize a linker-namespace for it.
- *
- * @hide
- */
- public static PathClassLoader createClassLoader(String dexPath,
- String librarySearchPath,
- String libraryPermittedPath,
- ClassLoader parent,
- int targetSdkVersion,
- boolean isNamespaceShared) {
- PathClassLoader pathClassloader = new PathClassLoader(dexPath, librarySearchPath, parent);
-
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "createClassloaderNamespace");
- String errorMessage = createClassloaderNamespace(pathClassloader,
- targetSdkVersion,
- librarySearchPath,
- libraryPermittedPath,
- isNamespaceShared);
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-
- if (errorMessage != null) {
- throw new UnsatisfiedLinkError("Unable to create namespace for the classloader " +
- pathClassloader + ": " + errorMessage);
- }
-
- return pathClassloader;
- }
-
- private static native String createClassloaderNamespace(ClassLoader classLoader,
- int targetSdkVersion,
- String librarySearchPath,
- String libraryPermittedPath,
- boolean isNamespaceShared);
-}
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index b9022bc..afd3c10 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -515,15 +515,12 @@
* namespace, i.e., this classloader can access platform-private native libraries. The
* classloader will use java.library.path as the native library path.
*/
- static PathClassLoader createPathClassLoader(String classPath, int targetSdkVersion) {
- String libraryPath = System.getProperty("java.library.path");
+ static ClassLoader createPathClassLoader(String classPath, int targetSdkVersion) {
+ String libraryPath = System.getProperty("java.library.path");
- return PathClassLoaderFactory.createClassLoader(classPath,
- libraryPath,
- libraryPath,
- ClassLoader.getSystemClassLoader(),
- targetSdkVersion,
- true /* isNamespaceShared */);
+ return ClassLoaderFactory.createClassLoader(classPath, libraryPath, libraryPath,
+ ClassLoader.getSystemClassLoader(), targetSdkVersion, true /* isNamespaceShared */,
+ null /* classLoaderName */);
}
/**
diff --git a/core/java/com/android/internal/util/FastXmlSerializer.java b/core/java/com/android/internal/util/FastXmlSerializer.java
index 3c1d2d6..b85b84f 100644
--- a/core/java/com/android/internal/util/FastXmlSerializer.java
+++ b/core/java/com/android/internal/util/FastXmlSerializer.java
@@ -49,18 +49,19 @@
null, null, null, null, "<", null, ">", null, // 56-63
};
- private static final int BUFFER_LEN = 8192;
+ private static final int DEFAULT_BUFFER_LEN = 32*1024;
private static String sSpace = " ";
- private final char[] mText = new char[BUFFER_LEN];
+ private final int mBufferLen;
+ private final char[] mText;
private int mPos;
private Writer mWriter;
private OutputStream mOutputStream;
private CharsetEncoder mCharset;
- private ByteBuffer mBytes = ByteBuffer.allocate(BUFFER_LEN);
+ private ByteBuffer mBytes;
private boolean mIndent = false;
private boolean mInTag;
@@ -68,9 +69,25 @@
private int mNesting = 0;
private boolean mLineStart = true;
+ public FastXmlSerializer() {
+ this(DEFAULT_BUFFER_LEN);
+ }
+
+ /**
+ * Allocate a FastXmlSerializer with the given internal output buffer size. If the
+ * size is zero or negative, then the default buffer size will be used.
+ *
+ * @param bufferSize Size in bytes of the in-memory output buffer that the writer will use.
+ */
+ public FastXmlSerializer(int bufferSize) {
+ mBufferLen = (bufferSize > 0) ? bufferSize : DEFAULT_BUFFER_LEN;
+ mText = new char[mBufferLen];
+ mBytes = ByteBuffer.allocate(mBufferLen);
+ }
+
private void append(char c) throws IOException {
int pos = mPos;
- if (pos >= (BUFFER_LEN-1)) {
+ if (pos >= (mBufferLen-1)) {
flush();
pos = mPos;
}
@@ -79,17 +96,17 @@
}
private void append(String str, int i, final int length) throws IOException {
- if (length > BUFFER_LEN) {
+ if (length > mBufferLen) {
final int end = i + length;
while (i < end) {
- int next = i + BUFFER_LEN;
- append(str, i, next<end ? BUFFER_LEN : (end-i));
+ int next = i + mBufferLen;
+ append(str, i, next<end ? mBufferLen : (end-i));
i = next;
}
return;
}
int pos = mPos;
- if ((pos+length) > BUFFER_LEN) {
+ if ((pos+length) > mBufferLen) {
flush();
pos = mPos;
}
@@ -98,17 +115,17 @@
}
private void append(char[] buf, int i, final int length) throws IOException {
- if (length > BUFFER_LEN) {
+ if (length > mBufferLen) {
final int end = i + length;
while (i < end) {
- int next = i + BUFFER_LEN;
- append(buf, i, next<end ? BUFFER_LEN : (end-i));
+ int next = i + mBufferLen;
+ append(buf, i, next<end ? mBufferLen : (end-i));
i = next;
}
return;
}
int pos = mPos;
- if ((pos+length) > BUFFER_LEN) {
+ if ((pos+length) > mBufferLen) {
flush();
pos = mPos;
}
diff --git a/core/java/com/android/internal/util/Protocol.java b/core/java/com/android/internal/util/Protocol.java
index b075db8..1aa32cc 100644
--- a/core/java/com/android/internal/util/Protocol.java
+++ b/core/java/com/android/internal/util/Protocol.java
@@ -65,5 +65,6 @@
public static final int BASE_NETWORK_MONITOR = 0x00082000;
public static final int BASE_NETWORK_FACTORY = 0x00083000;
public static final int BASE_ETHERNET = 0x00084000;
+ public static final int BASE_LOWPAN = 0x00085000;
//TODO: define all used protocols
}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index c4533c3..44dfb33 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -185,8 +185,8 @@
"android_content_res_Configuration.cpp",
"android_animation_PropertyValuesHolder.cpp",
"com_android_internal_net_NetworkStatsFactory.cpp",
+ "com_android_internal_os_ClassLoaderFactory.cpp",
"com_android_internal_os_FuseAppLoop.cpp",
- "com_android_internal_os_PathClassLoaderFactory.cpp",
"com_android_internal_os_Zygote.cpp",
"com_android_internal_util_VirtualRefBasePtr.cpp",
"com_android_internal_view_animation_NativeInterpolatorFactoryHelper.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 89e137b..30d4e02 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -203,8 +203,8 @@
extern int register_android_animation_PropertyValuesHolder(JNIEnv *env);
extern int register_com_android_internal_content_NativeLibraryHelper(JNIEnv *env);
extern int register_com_android_internal_net_NetworkStatsFactory(JNIEnv *env);
+extern int register_com_android_internal_os_ClassLoaderFactory(JNIEnv* env);
extern int register_com_android_internal_os_FuseAppLoop(JNIEnv* env);
-extern int register_com_android_internal_os_PathClassLoaderFactory(JNIEnv* env);
extern int register_com_android_internal_os_Zygote(JNIEnv *env);
extern int register_com_android_internal_util_VirtualRefBasePtr(JNIEnv *env);
@@ -1402,7 +1402,7 @@
REG_JNI(register_android_net_TrafficStats),
REG_JNI(register_android_os_MemoryFile),
REG_JNI(register_android_os_SharedMemory),
- REG_JNI(register_com_android_internal_os_PathClassLoaderFactory),
+ REG_JNI(register_com_android_internal_os_ClassLoaderFactory),
REG_JNI(register_com_android_internal_os_Zygote),
REG_JNI(register_com_android_internal_util_VirtualRefBasePtr),
REG_JNI(register_android_hardware_Camera),
diff --git a/core/jni/com_android_internal_os_PathClassLoaderFactory.cpp b/core/jni/com_android_internal_os_ClassLoaderFactory.cpp
similarity index 85%
rename from core/jni/com_android_internal_os_PathClassLoaderFactory.cpp
rename to core/jni/com_android_internal_os_ClassLoaderFactory.cpp
index a7a912c..7052e5f 100644
--- a/core/jni/com_android_internal_os_PathClassLoaderFactory.cpp
+++ b/core/jni/com_android_internal_os_ClassLoaderFactory.cpp
@@ -39,13 +39,13 @@
reinterpret_cast<void*>(createClassloaderNamespace_native) },
};
-static const char* const kPathClassLoaderFactoryPathName = "com/android/internal/os/PathClassLoaderFactory";
+static const char* const kClassLoaderFactoryPathName = "com/android/internal/os/ClassLoaderFactory";
namespace android
{
-int register_com_android_internal_os_PathClassLoaderFactory(JNIEnv* env) {
- return RegisterMethodsOrDie(env, kPathClassLoaderFactoryPathName, g_methods, NELEM(g_methods));
+int register_com_android_internal_os_ClassLoaderFactory(JNIEnv* env) {
+ return RegisterMethodsOrDie(env, kClassLoaderFactoryPathName, g_methods, NELEM(g_methods));
}
} // namespace android
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index a3cb350..55b57e93 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3215,6 +3215,11 @@
<permission android:name="android.permission.MANAGE_NOTIFICATIONS"
android:protectionLevel="signature" />
+ <!-- Allows notifications to be colorized
+ <p>Not for use by third-party applications. @hide -->
+ <permission android:name="android.permission.USE_COLORIZED_NOTIFICATIONS"
+ android:protectionLevel="signature|setup" />
+
<!-- Allows access to keyguard secure storage. Only allowed for system processes.
@hide -->
<permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE"
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 01b8e15..33f69b4 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1051,6 +1051,22 @@
<p>The default value of this attribute is <code>false</code>. -->
<attr name="isolatedSplits" format="boolean" />
+ <!-- The classname of the classloader used to load the application's classes
+ from its APK. The APK in question can either be the 'base' APK or any
+ of the application's 'split' APKs if it's using a feature split.
+
+ <p>
+ The supported values for this attribute are
+ <code>dalvik.system.PathClassLoader</code> and
+ <code>dalvik.system.DelegateLastClassLoader</code>. If unspecified,
+ the default value of this attribute is <code>dalvik.system.PathClassLoader</code>.
+
+ If an unknown classloader is provided, a PackageParserException with cause
+ <code>PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED</code> will be
+ thrown and the app will not be installed.
+ -->
+ <attr name="classLoader" format="string" />
+
<!-- If set to <code>true</code>, indicates to the platform that this APK is
a 'feature' split and that it implicitly depends on the base APK. This distinguishes
this split APK from a 'configuration' split, which provides resource overrides
@@ -1485,6 +1501,9 @@
<enum name="productivity" value="7" />
</attr>
+ <!-- Declares the kind of classloader this application's classes must be loaded with -->
+ <attr name="classLoader" />
+
</declare-styleable>
<!-- The <code>permission</code> tag declares a security permission that can be
used to control access from other packages to specific components or
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 802f027..ba5af2f 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1332,7 +1332,7 @@
permission.
[This is only used if config_enableUpdateableTimeZoneRules and
config_timeZoneRulesUpdateTrackingEnabled are true.] -->
- <string name="config_timeZoneRulesUpdaterPackage" translateable="false"></string>
+ <string name="config_timeZoneRulesUpdaterPackage" translateable="false">com.android.timezone.updater</string>
<!-- The package of the time zone rules data application. Expected to be configured
by OEMs to reference their own priv-app APK package.
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 2a4881d..634d79a 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2845,6 +2845,7 @@
<public-group type="attr" first-id="0x01010569">
<public name="showWhenLocked"/>
<public name="turnScreenOn"/>
+ <public name="classLoader" />
</public-group>
<public-group type="style" first-id="0x010302e0">
diff --git a/core/tests/coretests/src/android/app/NotificationTest.java b/core/tests/coretests/src/android/app/NotificationTest.java
index ac1ac19..5457713 100644
--- a/core/tests/coretests/src/android/app/NotificationTest.java
+++ b/core/tests/coretests/src/android/app/NotificationTest.java
@@ -18,6 +18,7 @@
import static com.android.internal.util.NotificationColorUtil.satisfiesTextContrast;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import android.content.Context;
@@ -26,8 +27,6 @@
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
-import com.android.internal.util.NotificationColorUtil;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -44,9 +43,51 @@
}
@Test
+ public void testColorizedByPermission() {
+ Notification n = new Notification.Builder(mContext, "test")
+ .setFlag(Notification.FLAG_CAN_COLORIZE, true)
+ .setColorized(true)
+ .build();
+ assertTrue(n.isColorized());
+
+ n = new Notification.Builder(mContext, "test")
+ .setFlag(Notification.FLAG_CAN_COLORIZE, true)
+ .build();
+ assertFalse(n.isColorized());
+
+ n = new Notification.Builder(mContext, "test")
+ .setFlag(Notification.FLAG_CAN_COLORIZE, false)
+ .setColorized(true)
+ .build();
+ assertFalse(n.isColorized());
+ }
+
+ @Test
+ public void testColorizedByForeground() {
+ Notification n = new Notification.Builder(mContext, "test")
+ .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true)
+ .setColorized(true)
+ .build();
+ assertTrue(n.isColorized());
+
+ n = new Notification.Builder(mContext, "test")
+ .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true)
+ .build();
+ assertFalse(n.isColorized());
+
+ n = new Notification.Builder(mContext, "test")
+ .setFlag(Notification.FLAG_FOREGROUND_SERVICE, false)
+ .setColorized(true)
+ .build();
+ assertFalse(n.isColorized());
+ }
+
+ @Test
public void testColorSatisfiedWhenBgDarkTextDarker() {
Notification.Builder builder = getMediaNotification();
- builder.build();
+ Notification n = builder.build();
+
+ assertTrue(n.isColorized());
// An initial guess where the foreground color is actually darker than an already dark bg
int backgroundColor = 0xff585868;
@@ -58,6 +99,45 @@
assertTrue(satisfiesTextContrast(secondaryTextColor, backgroundColor));
}
+ @Test
+ public void testHasCompletedProgress_noProgress() {
+ Notification n = new Notification.Builder(mContext).build();
+
+ assertFalse(n.hasCompletedProgress());
+ }
+
+ @Test
+ public void testHasCompletedProgress_complete() {
+ Notification n = new Notification.Builder(mContext)
+ .setProgress(100, 100, true)
+ .build();
+ Notification n2 = new Notification.Builder(mContext)
+ .setProgress(10, 10, false)
+ .build();
+ assertTrue(n.hasCompletedProgress());
+ assertTrue(n2.hasCompletedProgress());
+ }
+
+ @Test
+ public void testHasCompletedProgress_notComplete() {
+ Notification n = new Notification.Builder(mContext)
+ .setProgress(100, 99, true)
+ .build();
+ Notification n2 = new Notification.Builder(mContext)
+ .setProgress(10, 4, false)
+ .build();
+ assertFalse(n.hasCompletedProgress());
+ assertFalse(n2.hasCompletedProgress());
+ }
+
+ @Test
+ public void testHasCompletedProgress_zeroMax() {
+ Notification n = new Notification.Builder(mContext)
+ .setProgress(0, 0, true)
+ .build();
+ assertFalse(n.hasCompletedProgress());
+ }
+
private Notification.Builder getMediaNotification() {
MediaSession session = new MediaSession(mContext, "test");
return new Notification.Builder(mContext, "color")
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 956c862..2f064c9f 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -465,11 +465,7 @@
Settings.Secure.PARENTAL_CONTROL_LAST_UPDATE,
Settings.Secure.PAYMENT_SERVICE_SEARCH_URI,
Settings.Secure.PRINT_SERVICE_SEARCH_URI,
- Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK, // Candidate?
- Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP, // Candidate?
- Settings.Secure.SCREENSAVER_COMPONENTS,
Settings.Secure.SCREENSAVER_DEFAULT_COMPONENT, // Candidate?
- Settings.Secure.SCREENSAVER_ENABLED, // Candidate?
Settings.Secure.SEARCH_GLOBAL_SEARCH_ACTIVITY,
Settings.Secure.SEARCH_MAX_RESULTS_PER_SOURCE,
Settings.Secure.SEARCH_MAX_RESULTS_TO_DISPLAY,
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 19d2a31..85ea1ff 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -163,6 +163,8 @@
<!-- This is a list of all the libraries available for application
code to link against. -->
+ <library name="android.test.mock"
+ file="/system/framework/android.test.mock.jar" />
<library name="android.test.runner"
file="/system/framework/android.test.runner.jar" />
<library name="javax.obex"
diff --git a/lowpan/Android.mk b/lowpan/Android.mk
index 9e9164f..0079958 100644
--- a/lowpan/Android.mk
+++ b/lowpan/Android.mk
@@ -28,4 +28,6 @@
LOCAL_AIDL_INCLUDES += frameworks/base/core/java
LOCAL_SRC_FILES += $(call all-Iaidl-files-under, java/android/net/lowpan)
include $(BUILD_SHARED_LIBRARY)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
endif
diff --git a/lowpan/java/android/net/lowpan/ILowpanInterface.aidl b/lowpan/java/android/net/lowpan/ILowpanInterface.aidl
index 647fcc1..329e9fa 100644
--- a/lowpan/java/android/net/lowpan/ILowpanInterface.aidl
+++ b/lowpan/java/android/net/lowpan/ILowpanInterface.aidl
@@ -37,41 +37,79 @@
//////////////////////////////////////////////////////////////////////////
// Property Key Constants
+ /** Type: Boolean */
const String KEY_INTERFACE_ENABLED = "android.net.lowpan.property.INTERFACE_ENABLED";
+
+ /** Type: Boolean */
const String KEY_INTERFACE_UP = "android.net.lowpan.property.INTERFACE_UP";
+
+ /** Type: Boolean */
const String KEY_INTERFACE_COMMISSIONED = "android.net.lowpan.property.INTERFACE_COMMISSIONED";
+
+ /** Type: Boolean */
const String KEY_INTERFACE_CONNECTED = "android.net.lowpan.property.INTERFACE_CONNECTED";
+
+ /** Type: String */
const String KEY_INTERFACE_STATE = "android.net.lowpan.property.INTERFACE_STATE";
+ /** Type: String */
const String KEY_NETWORK_NAME = "android.net.lowpan.property.NETWORK_NAME";
+
+ /** Type: Integer */
const String KEY_NETWORK_TYPE = "android.net.lowpan.property.NETWORK_TYPE";
+
+ /** Type: Integer */
const String KEY_NETWORK_PANID = "android.net.lowpan.property.NETWORK_PANID";
+
+ /** Type: byte[] */
const String KEY_NETWORK_XPANID = "android.net.lowpan.property.NETWORK_XPANID";
+
+ /** Type: String */
const String KEY_NETWORK_ROLE = "android.net.lowpan.property.NETWORK_ROLE";
+
+ /** Type: byte[] */
const String KEY_NETWORK_MASTER_KEY = "android.net.lowpan.property.NETWORK_MASTER_KEY";
+
+ /** Type: Integer */
const String KEY_NETWORK_MASTER_KEY_INDEX
= "android.net.lowpan.property.NETWORK_MASTER_KEY_INDEX";
+ /** Type: int[] */
const String KEY_SUPPORTED_CHANNELS = "android.net.lowpan.property.SUPPORTED_CHANNELS";
+
+ /** Type: Integer */
const String KEY_CHANNEL = "android.net.lowpan.property.CHANNEL";
+
+ /** Type: int[] */
const String KEY_CHANNEL_MASK = "android.net.lowpan.property.CHANNEL_MASK";
+
+ /** Type: Integer */
const String KEY_MAX_TX_POWER = "android.net.lowpan.property.MAX_TX_POWER";
+
+ /** Type: Integer */
const String KEY_RSSI = "android.net.lowpan.property.RSSI";
+
+ /** Type: Integer */
const String KEY_LQI = "android.net.lowpan.property.LQI";
- const String KEY_LINK_ADDRESS_ARRAY = "android.net.lowpan.property.LINK_ADDRESS_ARRAY";
- const String KEY_ROUTE_INFO_ARRAY = "android.net.lowpan.property.ROUTE_INFO_ARRAY";
-
+ /** Type: byte[] */
const String KEY_BEACON_ADDRESS = "android.net.lowpan.property.BEACON_ORIGIN_ADDRESS";
+
+ /** Type: Boolean */
const String KEY_BEACON_CAN_ASSIST = "android.net.lowpan.property.BEACON_CAN_ASSIST";
+ /** Type: String */
const String DRIVER_VERSION = "android.net.lowpan.property.DRIVER_VERSION";
+
+ /** Type: String */
const String NCP_VERSION = "android.net.lowpan.property.NCP_VERSION";
- /** @hide */
+ /** Type: byte[]
+ * @hide */
const String KEY_EXTENDED_ADDRESS = "android.net.lowpan.property.EXTENDED_ADDRESS";
- /** @hide */
+ /** Type: byte[]
+ * @hide */
const String KEY_MAC_ADDRESS = "android.net.lowpan.property.MAC_ADDRESS";
//////////////////////////////////////////////////////////////////////////
@@ -144,6 +182,9 @@
void startEnergyScan(in Map properties, ILowpanEnergyScanCallback listener);
oneway void stopEnergyScan();
+ String[] copyLinkAddresses();
+ IpPrefix[] copyLinkNetworks();
+
void addOnMeshPrefix(in IpPrefix prefix, int flags);
oneway void removeOnMeshPrefix(in IpPrefix prefix);
diff --git a/lowpan/java/android/net/lowpan/ILowpanInterfaceListener.aidl b/lowpan/java/android/net/lowpan/ILowpanInterfaceListener.aidl
index c99d732..0ae01a1 100644
--- a/lowpan/java/android/net/lowpan/ILowpanInterfaceListener.aidl
+++ b/lowpan/java/android/net/lowpan/ILowpanInterfaceListener.aidl
@@ -16,7 +16,15 @@
package android.net.lowpan;
+import android.net.IpPrefix;
+
/** {@hide} */
interface ILowpanInterfaceListener {
oneway void onPropertiesChanged(in Map properties);
+
+ oneway void onLinkNetworkAdded(in IpPrefix prefix);
+ oneway void onLinkNetworkRemoved(in IpPrefix prefix);
+
+ oneway void onLinkAddressAdded(in String address);
+ oneway void onLinkAddressRemoved(in String address);
}
diff --git a/lowpan/java/android/net/lowpan/ILowpanManager.aidl b/lowpan/java/android/net/lowpan/ILowpanManager.aidl
index 5a8d7dc..326aa65 100644
--- a/lowpan/java/android/net/lowpan/ILowpanManager.aidl
+++ b/lowpan/java/android/net/lowpan/ILowpanManager.aidl
@@ -21,6 +21,7 @@
/** {@hide} */
interface ILowpanManager {
+ /* Keep this in sync with Context.LOWPAN_SERVICE */
const String LOWPAN_SERVICE_NAME = "lowpan";
ILowpanInterface getInterface(@utf8InCpp String name);
diff --git a/lowpan/java/android/net/lowpan/LowpanException.java b/lowpan/java/android/net/lowpan/LowpanException.java
index b43b2fe..5a1f729 100644
--- a/lowpan/java/android/net/lowpan/LowpanException.java
+++ b/lowpan/java/android/net/lowpan/LowpanException.java
@@ -16,8 +16,6 @@
package android.net.lowpan;
-import android.os.DeadObjectException;
-import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.util.AndroidException;
@@ -49,92 +47,76 @@
public static final int LOWPAN_JOIN_FAILED_AT_AUTH = 16;
public static final int LOWPAN_FORM_FAILED_AT_SCAN = 17;
- /**
- * Convert ServiceSpecificExceptions and Binder RemoteExceptions from LoWPAN binder interfaces
- * into the correct public exceptions.
- *
- * @hide
- */
- public static void throwAsPublicException(Throwable t) throws LowpanException {
- if (t instanceof ServiceSpecificException) {
- ServiceSpecificException e = (ServiceSpecificException) t;
- int reason;
- switch (e.errorCode) {
- case ILowpanInterface.ERROR_INVALID_ARGUMENT:
- case ILowpanInterface.ERROR_INVALID_TYPE:
- case ILowpanInterface.ERROR_INVALID_VALUE:
- throw new IllegalArgumentException(e.getMessage(), e);
+ public static LowpanException rethrowAsLowpanException(ServiceSpecificException e)
+ throws LowpanException {
+ int reason;
+ switch (e.errorCode) {
+ case ILowpanInterface.ERROR_INVALID_ARGUMENT:
+ case ILowpanInterface.ERROR_INVALID_TYPE:
+ case ILowpanInterface.ERROR_INVALID_VALUE:
+ throw new IllegalArgumentException(e.getMessage(), e);
- case ILowpanInterface.ERROR_PERMISSION_DENIED:
- throw new SecurityException(e.getMessage(), e);
+ case ILowpanInterface.ERROR_PERMISSION_DENIED:
+ throw new SecurityException(e.getMessage(), e);
- case ILowpanInterface.ERROR_DISABLED:
- reason = LowpanException.LOWPAN_DISABLED;
- break;
+ case ILowpanInterface.ERROR_DISABLED:
+ reason = LowpanException.LOWPAN_DISABLED;
+ break;
- case ILowpanInterface.ERROR_WRONG_STATE:
- reason = LowpanException.LOWPAN_WRONG_STATE;
- break;
+ case ILowpanInterface.ERROR_WRONG_STATE:
+ reason = LowpanException.LOWPAN_WRONG_STATE;
+ break;
- case ILowpanInterface.ERROR_BUSY:
- reason = LowpanException.LOWPAN_BUSY;
- break;
+ case ILowpanInterface.ERROR_BUSY:
+ reason = LowpanException.LOWPAN_BUSY;
+ break;
- case ILowpanInterface.ERROR_ALREADY:
- reason = LowpanException.LOWPAN_ALREADY;
- break;
+ case ILowpanInterface.ERROR_ALREADY:
+ reason = LowpanException.LOWPAN_ALREADY;
+ break;
- case ILowpanInterface.ERROR_CANCELED:
- reason = LowpanException.LOWPAN_CANCELED;
- break;
+ case ILowpanInterface.ERROR_CANCELED:
+ reason = LowpanException.LOWPAN_CANCELED;
+ break;
- case ILowpanInterface.ERROR_CREDENTIAL_NEEDED:
- reason = LowpanException.LOWPAN_CREDENTIAL_NEEDED;
- break;
+ case ILowpanInterface.ERROR_CREDENTIAL_NEEDED:
+ reason = LowpanException.LOWPAN_CREDENTIAL_NEEDED;
+ break;
- case ILowpanInterface.ERROR_FEATURE_NOT_SUPPORTED:
- reason = LowpanException.LOWPAN_FEATURE_NOT_SUPPORTED;
- break;
+ case ILowpanInterface.ERROR_FEATURE_NOT_SUPPORTED:
+ reason = LowpanException.LOWPAN_FEATURE_NOT_SUPPORTED;
+ break;
- case ILowpanInterface.ERROR_PROPERTY_NOT_FOUND:
- reason = LowpanException.LOWPAN_PROPERTY_NOT_FOUND;
- break;
+ case ILowpanInterface.ERROR_PROPERTY_NOT_FOUND:
+ reason = LowpanException.LOWPAN_PROPERTY_NOT_FOUND;
+ break;
- case ILowpanInterface.ERROR_JOIN_FAILED_UNKNOWN:
- reason = LowpanException.LOWPAN_JOIN_FAILED_UNKNOWN;
- break;
+ case ILowpanInterface.ERROR_JOIN_FAILED_UNKNOWN:
+ reason = LowpanException.LOWPAN_JOIN_FAILED_UNKNOWN;
+ break;
- case ILowpanInterface.ERROR_JOIN_FAILED_AT_SCAN:
- reason = LowpanException.LOWPAN_JOIN_FAILED_AT_SCAN;
- break;
+ case ILowpanInterface.ERROR_JOIN_FAILED_AT_SCAN:
+ reason = LowpanException.LOWPAN_JOIN_FAILED_AT_SCAN;
+ break;
- case ILowpanInterface.ERROR_JOIN_FAILED_AT_AUTH:
- reason = LowpanException.LOWPAN_JOIN_FAILED_AT_AUTH;
- break;
+ case ILowpanInterface.ERROR_JOIN_FAILED_AT_AUTH:
+ reason = LowpanException.LOWPAN_JOIN_FAILED_AT_AUTH;
+ break;
- case ILowpanInterface.ERROR_FORM_FAILED_AT_SCAN:
- reason = LowpanException.LOWPAN_FORM_FAILED_AT_SCAN;
- break;
+ case ILowpanInterface.ERROR_FORM_FAILED_AT_SCAN:
+ reason = LowpanException.LOWPAN_FORM_FAILED_AT_SCAN;
+ break;
- case ILowpanInterface.ERROR_TIMEOUT:
- case ILowpanInterface.ERROR_NCP_PROBLEM:
- reason = LowpanException.LOWPAN_NCP_PROBLEM;
- break;
- case ILowpanInterface.ERROR_UNSPECIFIED:
- default:
- reason = LOWPAN_ERROR;
- break;
- }
- throw new LowpanException(reason, e.getMessage(), e);
- } else if (t instanceof DeadObjectException) {
- throw new LowpanException(LOWPAN_DEAD, t);
- } else if (t instanceof RemoteException) {
- throw new UnsupportedOperationException(
- "An unknown RemoteException was thrown" + " which should never happen.", t);
- } else if (t instanceof RuntimeException) {
- RuntimeException e = (RuntimeException) t;
- throw e;
+ case ILowpanInterface.ERROR_TIMEOUT:
+ case ILowpanInterface.ERROR_NCP_PROBLEM:
+ reason = LowpanException.LOWPAN_NCP_PROBLEM;
+ break;
+ case ILowpanInterface.ERROR_UNSPECIFIED:
+ default:
+ reason = LOWPAN_ERROR;
+ break;
}
+ throw new LowpanException(reason, e.getMessage(), e);
}
private final int mReason;
diff --git a/lowpan/java/android/net/lowpan/LowpanIdentity.java b/lowpan/java/android/net/lowpan/LowpanIdentity.java
index 9be45ef..2d36f7f 100644
--- a/lowpan/java/android/net/lowpan/LowpanIdentity.java
+++ b/lowpan/java/android/net/lowpan/LowpanIdentity.java
@@ -53,7 +53,7 @@
}
public Builder setXpanid(byte x[]) {
- identity.mXpanid = x.clone();
+ identity.mXpanid = (x != null ? x.clone() : null);
return this;
}
@@ -115,7 +115,7 @@
}
public byte[] getXpanid() {
- return mXpanid.clone();
+ return mXpanid != null ? mXpanid.clone() : null;
}
public int getPanid() {
diff --git a/lowpan/java/android/net/lowpan/LowpanInterface.java b/lowpan/java/android/net/lowpan/LowpanInterface.java
index 2bb4ecd..55bf399 100644
--- a/lowpan/java/android/net/lowpan/LowpanInterface.java
+++ b/lowpan/java/android/net/lowpan/LowpanInterface.java
@@ -18,9 +18,12 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.Context;
import android.net.IpPrefix;
+import android.net.LinkAddress;
+import android.os.DeadObjectException;
import android.os.Handler;
-import android.os.IBinder;
+import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.util.Log;
@@ -188,60 +191,35 @@
public void onPropertiesChanged(@NonNull Map properties) {}
}
- private ILowpanInterface mBinder;
+ private final ILowpanInterface mBinder;
+ private final Looper mLooper;
private final HashMap<Integer, ILowpanInterfaceListener> mListenerMap = new HashMap<>();
- /** Map between IBinder identity hashes and LowpanInstance objects. */
- private static final HashMap<Integer, LowpanInterface> sInstanceMap = new HashMap<>();
+ /**
+ * Create a new LowpanInterface instance. Applications will almost always want to use {@link
+ * LowpanManager#getInterface LowpanManager.getInterface()} instead of this.
+ *
+ * @param context the application context
+ * @param service the Binder interface
+ * @param looper the Binder interface
+ * @hide
+ */
+ public LowpanInterface(Context context, ILowpanInterface service, Looper looper) {
+ /* We aren't currently using the context, but if we need
+ * it later on we can easily add it to the class.
+ */
- private LowpanInterface(IBinder binder) {
- mBinder = ILowpanInterface.Stub.asInterface(binder);
+ mBinder = service;
+ mLooper = looper;
}
/**
- * Get the LowpanInterface object associated with this IBinder. Returns null if this IBinder
- * does not implement the appropriate interface.
+ * Returns the ILowpanInterface object associated with this interface.
*
* @hide
*/
- @NonNull
- public static final LowpanInterface from(IBinder binder) {
- Integer hashCode = Integer.valueOf(System.identityHashCode(binder));
- LowpanInterface instance;
-
- synchronized (sInstanceMap) {
- instance = sInstanceMap.get(hashCode);
-
- if (instance == null) {
- instance = new LowpanInterface(binder);
- sInstanceMap.put(hashCode, instance);
- }
- }
-
- return instance;
- }
-
- /** {@hide} */
- public static final LowpanInterface from(ILowpanInterface iface) {
- return from(iface.asBinder());
- }
-
- /** {@hide} */
- public static final LowpanInterface getInterfaceFromBinder(IBinder binder) {
- return from(binder);
- }
-
- /**
- * Returns the IBinder object associated with this interface.
- *
- * @hide
- */
- public IBinder getBinder() {
- return mBinder.asBinder();
- }
-
- private static void throwAsPublicException(Throwable t) throws LowpanException {
- LowpanException.throwAsPublicException(t);
+ public ILowpanInterface getService() {
+ return mBinder;
}
// Private Property Helpers
@@ -251,11 +229,10 @@
mBinder.setProperties(properties);
} catch (RemoteException x) {
- // Catch and ignore all binder exceptions
- Log.e(TAG, x.toString());
+ throw x.rethrowAsRuntimeException();
} catch (ServiceSpecificException x) {
- throwAsPublicException(x);
+ throw LowpanException.rethrowAsLowpanException(x);
}
}
@@ -263,13 +240,13 @@
Map<String, Object> getProperties(String keys[]) throws LowpanException {
try {
return mBinder.getProperties(keys);
+
} catch (RemoteException x) {
- // Catch and ignore all binder exceptions
- Log.e(TAG, x.toString());
+ throw x.rethrowAsRuntimeException();
+
} catch (ServiceSpecificException x) {
- throwAsPublicException(x);
+ throw LowpanException.rethrowAsLowpanException(x);
}
- return new HashMap();
}
/** @hide */
@@ -294,13 +271,13 @@
<T> String getPropertyAsString(LowpanProperty<T> key) throws LowpanException {
try {
return mBinder.getPropertyAsString(key.getName());
+
} catch (RemoteException x) {
- // Catch and ignore all binder exceptions
- Log.e(TAG, x.toString());
+ throw x.rethrowAsRuntimeException();
+
} catch (ServiceSpecificException x) {
- throwAsPublicException(x);
+ throw LowpanException.rethrowAsLowpanException(x);
}
- return null;
}
int getPropertyAsInt(LowpanProperty<Integer> key) throws LowpanException {
@@ -332,10 +309,12 @@
Map<String, Object> parameters = new HashMap();
provision.addToMap(parameters);
mBinder.form(parameters);
+
} catch (RemoteException x) {
- throwAsPublicException(x);
+ throw x.rethrowAsRuntimeException();
+
} catch (ServiceSpecificException x) {
- throwAsPublicException(x);
+ throw LowpanException.rethrowAsLowpanException(x);
}
}
@@ -352,10 +331,12 @@
Map<String, Object> parameters = new HashMap();
provision.addToMap(parameters);
mBinder.join(parameters);
+
} catch (RemoteException x) {
- throwAsPublicException(x);
+ throw x.rethrowAsRuntimeException();
+
} catch (ServiceSpecificException x) {
- throwAsPublicException(x);
+ throw LowpanException.rethrowAsLowpanException(x);
}
}
@@ -386,10 +367,12 @@
public void leave() throws LowpanException {
try {
mBinder.leave();
+
} catch (RemoteException x) {
- throwAsPublicException(x);
+ throw x.rethrowAsRuntimeException();
+
} catch (ServiceSpecificException x) {
- throwAsPublicException(x);
+ throw LowpanException.rethrowAsLowpanException(x);
}
}
@@ -415,34 +398,26 @@
public void reset() throws LowpanException {
try {
mBinder.reset();
+
} catch (RemoteException x) {
- throwAsPublicException(x);
+ throw x.rethrowAsRuntimeException();
+
} catch (ServiceSpecificException x) {
- throwAsPublicException(x);
+ throw LowpanException.rethrowAsLowpanException(x);
}
}
// Public Getters and Setters
- /**
- * Returns the name of this network interface.
- *
- * <p>Will return empty string if this interface is no longer viable.
- */
+ /** Returns the name of this network interface. */
@NonNull
public String getName() {
try {
return mBinder.getName();
+
} catch (RemoteException x) {
- // Catch and ignore all binder exceptions
- // when fetching the name.
- Log.e(TAG, x.toString());
- } catch (ServiceSpecificException x) {
- // Catch and ignore all service-specific exceptions
- // when fetching the name.
- Log.e(TAG, x.toString());
+ throw x.rethrowAsRuntimeException();
}
- return "";
}
/**
@@ -640,58 +615,77 @@
public void registerCallback(@NonNull Callback cb, @Nullable Handler handler) {
ILowpanInterfaceListener.Stub listenerBinder =
new ILowpanInterfaceListener.Stub() {
- public void onPropertiesChanged(Map properties) {
+ private Handler mHandler;
+
+ {
+ if (handler != null) {
+ mHandler = handler;
+ } else if (mLooper != null) {
+ mHandler = new Handler(mLooper);
+ } else {
+ mHandler = new Handler();
+ }
+ }
+
+ @Override public void onPropertiesChanged(Map properties) {
Runnable runnable =
- new Runnable() {
- @Override
- public void run() {
- for (String key : (Set<String>) properties.keySet()) {
- Object value = properties.get(key);
- switch (key) {
- case ILowpanInterface.KEY_INTERFACE_ENABLED:
- cb.onEnabledChanged(
- ((Boolean) value).booleanValue());
- break;
- case ILowpanInterface.KEY_INTERFACE_UP:
- cb.onUpChanged(
- ((Boolean) value).booleanValue());
- break;
- case ILowpanInterface.KEY_INTERFACE_CONNECTED:
- cb.onConnectedChanged(
- ((Boolean) value).booleanValue());
- break;
- case ILowpanInterface.KEY_INTERFACE_STATE:
- cb.onStateChanged((String) value);
- break;
- case ILowpanInterface.KEY_NETWORK_NAME:
- case ILowpanInterface.KEY_NETWORK_PANID:
- case ILowpanInterface.KEY_NETWORK_XPANID:
- case ILowpanInterface.KEY_CHANNEL:
- cb.onLowpanIdentityChanged(getLowpanIdentity());
- break;
- case ILowpanInterface.KEY_NETWORK_ROLE:
- cb.onRoleChanged(value.toString());
- break;
- }
+ () -> {
+ for (String key : (Set<String>) properties.keySet()) {
+ Object value = properties.get(key);
+ switch (key) {
+ case ILowpanInterface.KEY_INTERFACE_ENABLED:
+ cb.onEnabledChanged(
+ ((Boolean) value).booleanValue());
+ break;
+ case ILowpanInterface.KEY_INTERFACE_UP:
+ cb.onUpChanged(((Boolean) value).booleanValue());
+ break;
+ case ILowpanInterface.KEY_INTERFACE_CONNECTED:
+ cb.onConnectedChanged(
+ ((Boolean) value).booleanValue());
+ break;
+ case ILowpanInterface.KEY_INTERFACE_STATE:
+ cb.onStateChanged((String) value);
+ break;
+ case ILowpanInterface.KEY_NETWORK_NAME:
+ case ILowpanInterface.KEY_NETWORK_PANID:
+ case ILowpanInterface.KEY_NETWORK_XPANID:
+ case ILowpanInterface.KEY_CHANNEL:
+ cb.onLowpanIdentityChanged(getLowpanIdentity());
+ break;
+ case ILowpanInterface.KEY_NETWORK_ROLE:
+ cb.onRoleChanged(value.toString());
+ break;
}
- cb.onPropertiesChanged(properties);
}
+ cb.onPropertiesChanged(properties);
};
- if (handler != null) {
- handler.post(runnable);
- } else {
- runnable.run();
- }
+ mHandler.post(runnable);
+ }
+
+ @Override public void onLinkNetworkAdded(IpPrefix prefix) {
+ // Support for this event isn't yet implemented.
+ }
+
+ @Override public void onLinkNetworkRemoved(IpPrefix prefix) {
+ // Support for this event isn't yet implemented.
+ }
+
+ @Override public void onLinkAddressAdded(String address) {
+ // Support for this event isn't yet implemented.
+ }
+
+ @Override public void onLinkAddressRemoved(String address) {
+ // Support for this event isn't yet implemented.
}
};
try {
mBinder.addListener(listenerBinder);
} catch (RemoteException x) {
- // Log and ignore. If this happens, this interface
- // is likely dead anyway.
- Log.e(TAG, x.toString());
+ throw x.rethrowAsRuntimeException();
}
+
synchronized (mListenerMap) {
mListenerMap.put(System.identityHashCode(cb), listenerBinder);
}
@@ -728,9 +722,11 @@
try {
mBinder.removeListener(listenerBinder);
+ } catch (DeadObjectException x) {
+ // We ignore a dead object exception because that
+ // pretty clearly means our callback isn't registered.
} catch (RemoteException x) {
- // Catch and ignore all binder exceptions
- Log.e(TAG, x.toString());
+ throw x.rethrowAsRuntimeException();
}
}
}
@@ -752,6 +748,46 @@
// Route Management
/**
+ * Makes a copy of the internal list of LinkAddresses.
+ *
+ * @hide
+ */
+ public LinkAddress[] copyLinkAddresses() throws LowpanException {
+ try {
+ String[] linkAddressStrings = mBinder.copyLinkAddresses();
+ LinkAddress[] ret = new LinkAddress[linkAddressStrings.length];
+ int i = 0;
+ for (String str : linkAddressStrings) {
+ ret[i++] = new LinkAddress(str);
+ }
+ return ret;
+
+ } catch (RemoteException x) {
+ throw x.rethrowAsRuntimeException();
+
+ } catch (ServiceSpecificException x) {
+ throw LowpanException.rethrowAsLowpanException(x);
+ }
+ }
+
+ /**
+ * Makes a copy of the internal list of networks reachable on via this link.
+ *
+ * @hide
+ */
+ public IpPrefix[] copyLinkNetworks() throws LowpanException {
+ try {
+ return mBinder.copyLinkNetworks();
+
+ } catch (RemoteException x) {
+ throw x.rethrowAsRuntimeException();
+
+ } catch (ServiceSpecificException x) {
+ throw LowpanException.rethrowAsLowpanException(x);
+ }
+ }
+
+ /**
* Advertise the given IP prefix as an on-mesh prefix.
*
* @hide
@@ -759,10 +795,12 @@
public void addOnMeshPrefix(IpPrefix prefix, int flags) throws LowpanException {
try {
mBinder.addOnMeshPrefix(prefix, flags);
+
} catch (RemoteException x) {
- throwAsPublicException(x);
+ throw x.rethrowAsRuntimeException();
+
} catch (ServiceSpecificException x) {
- throwAsPublicException(x);
+ throw LowpanException.rethrowAsLowpanException(x);
}
}
@@ -775,9 +813,10 @@
public void removeOnMeshPrefix(IpPrefix prefix) {
try {
mBinder.removeOnMeshPrefix(prefix);
+
} catch (RemoteException x) {
- // Catch and ignore all binder exceptions
- Log.e(TAG, x.toString());
+ throw x.rethrowAsRuntimeException();
+
} catch (ServiceSpecificException x) {
// Catch and ignore all service exceptions
Log.e(TAG, x.toString());
@@ -793,10 +832,12 @@
public void addExternalRoute(IpPrefix prefix, int flags) throws LowpanException {
try {
mBinder.addExternalRoute(prefix, flags);
+
} catch (RemoteException x) {
- throwAsPublicException(x);
+ throw x.rethrowAsRuntimeException();
+
} catch (ServiceSpecificException x) {
- throwAsPublicException(x);
+ throw LowpanException.rethrowAsLowpanException(x);
}
}
@@ -808,9 +849,10 @@
public void removeExternalRoute(IpPrefix prefix) {
try {
mBinder.removeExternalRoute(prefix);
+
} catch (RemoteException x) {
- // Catch and ignore all binder exceptions
- Log.e(TAG, x.toString());
+ throw x.rethrowAsRuntimeException();
+
} catch (ServiceSpecificException x) {
// Catch and ignore all service exceptions
Log.e(TAG, x.toString());
diff --git a/lowpan/java/android/net/lowpan/LowpanManager.java b/lowpan/java/android/net/lowpan/LowpanManager.java
index ecdda49..2d974ee 100644
--- a/lowpan/java/android/net/lowpan/LowpanManager.java
+++ b/lowpan/java/android/net/lowpan/LowpanManager.java
@@ -19,14 +19,15 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
-import android.os.DeadObjectException;
import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.util.AndroidException;
-import android.util.Log;
+import java.lang.ref.WeakReference;
import java.util.HashMap;
+import java.util.Map;
+import java.util.WeakHashMap;
/**
* Manager object for looking up LoWPAN interfaces.
@@ -45,72 +46,119 @@
public void onInterfaceRemoved(LowpanInterface lowpanInterface) {}
}
- private Context mContext;
- private ILowpanManager mManager;
- private HashMap<Integer, ILowpanManagerListener> mListenerMap = new HashMap<>();
+ private final Map<Integer, ILowpanManagerListener> mListenerMap = new HashMap<>();
+ private final Map<String, LowpanInterface> mInterfaceCache = new HashMap<>();
- private static LowpanManager sSingletonInstance;
+ /* This is a WeakHashMap because we don't want to hold onto
+ * a strong reference to ILowpanInterface, so that it can be
+ * garbage collected if it isn't being used anymore. Since
+ * the value class holds onto this specific ILowpanInterface,
+ * we also need to have a weak reference to the value.
+ * This design pattern allows us to skip removal of items
+ * from this Map without leaking memory.
+ */
+ private final Map<ILowpanInterface, WeakReference<LowpanInterface>> mBinderCache =
+ new WeakHashMap<>();
+
+ private final ILowpanManager mService;
+ private final Context mContext;
+ private final Looper mLooper;
// Static Methods
- /** Returns a reference to the LowpanManager object, allocating it if necessary. */
- public static LowpanManager getManager() {
- return from(null);
+ public static LowpanManager from(Context context) {
+ return (LowpanManager) context.getSystemService(Context.LOWPAN_SERVICE);
}
- public static LowpanManager from(Context context) {
- // TODO: Actually get this from the context!
+ /** @hide */
+ public static LowpanManager getManager() {
+ IBinder binder = ServiceManager.getService(Context.LOWPAN_SERVICE);
- if (sSingletonInstance == null) {
- sSingletonInstance = new LowpanManager();
+ if (binder != null) {
+ ILowpanManager service = ILowpanManager.Stub.asInterface(binder);
+ return new LowpanManager(service);
}
- return sSingletonInstance;
+
+ return null;
}
// Constructors
- /**
- * Private LowpanManager constructor. Since we are a singleton, we do not allow external
- * construction.
- */
- private LowpanManager() {}
-
- // Private Methods
-
- /**
- * Returns a reference to the ILowpanManager interface, provided by the LoWPAN Manager Service.
- */
- @Nullable
- private synchronized ILowpanManager getILowpanManager() {
- // Use a local reference of this object for thread safety.
- ILowpanManager manager = mManager;
-
- if (manager == null) {
- IBinder serviceBinder =
- new ServiceManager().getService(ILowpanManager.LOWPAN_SERVICE_NAME);
-
- manager = ILowpanManager.Stub.asInterface(serviceBinder);
-
- mManager = manager;
-
- // Add any listeners
- synchronized (mListenerMap) {
- for (ILowpanManagerListener listener : mListenerMap.values()) {
- try {
- manager.addListener(listener);
-
- } catch (RemoteException x) {
- // Consider any failure here as implying the manager is defunct
- mManager = null;
- manager = null;
- }
- }
- }
- }
- return manager;
+ LowpanManager(ILowpanManager service) {
+ mService = service;
+ mContext = null;
+ mLooper = null;
}
- // Public Methods
+ /**
+ * Create a new LowpanManager instance. Applications will almost always want to use {@link
+ * android.content.Context#getSystemService Context.getSystemService()} to retrieve the standard
+ * {@link android.content.Context#LOWPAN_SERVICE Context.LOWPAN_SERVICE}.
+ *
+ * @param context the application context
+ * @param service the Binder interface
+ * @param looper the default Looper to run callbacks on
+ * @hide - hide this because it takes in a parameter of type ILowpanManager, which is a system
+ * private class.
+ */
+ public LowpanManager(Context context, ILowpanManager service, Looper looper) {
+ mContext = context;
+ mService = service;
+ mLooper = looper;
+ }
+
+ /** @hide */
+ @Nullable
+ public LowpanInterface getInterface(@NonNull ILowpanInterface ifaceService) {
+ LowpanInterface iface = null;
+
+ try {
+ synchronized (mBinderCache) {
+ if (mBinderCache.containsKey(ifaceService)) {
+ iface = mBinderCache.get(ifaceService).get();
+ }
+
+ if (iface == null) {
+ String ifaceName = ifaceService.getName();
+
+ iface = new LowpanInterface(mContext, ifaceService, mLooper);
+
+ synchronized (mInterfaceCache) {
+ mInterfaceCache.put(iface.getName(), iface);
+ }
+
+ mBinderCache.put(ifaceService, new WeakReference(iface));
+
+ /* Make sure we remove the object from the
+ * interface cache if the associated service
+ * dies.
+ */
+ ifaceService
+ .asBinder()
+ .linkToDeath(
+ new IBinder.DeathRecipient() {
+ @Override
+ public void binderDied() {
+ synchronized (mInterfaceCache) {
+ LowpanInterface iface =
+ mInterfaceCache.get(ifaceName);
+
+ if ((iface != null)
+ && (iface.getService() == ifaceService)) {
+ mInterfaceCache.remove(ifaceName);
+ }
+ }
+ }
+ },
+ 0);
+ }
+ }
+ } catch (RemoteException x) {
+ throw x.rethrowAsRuntimeException();
+ }
+
+ return iface;
+ }
/**
* Returns a reference to the requested LowpanInterface object. If the given interface doesn't
@@ -118,27 +166,32 @@
*/
@Nullable
public LowpanInterface getInterface(@NonNull String name) {
- LowpanInterface ret = null;
- ILowpanManager manager = getILowpanManager();
+ LowpanInterface iface = null;
- // Maximum number of tries is two. We should only try
- // more than once if our manager has died or there
- // was some sort of AIDL buffer full event.
- for (int i = 0; i < 2 && manager != null; i++) {
- try {
- ILowpanInterface iface = manager.getInterface(name);
- if (iface != null) {
- ret = LowpanInterface.getInterfaceFromBinder(iface.asBinder());
+ try {
+ /* This synchronized block covers both branches of the enclosed
+ * if() statement in order to avoid a race condition. Two threads
+ * calling getInterface() with the same name would race to create
+ * the associated LowpanInterface object, creating two of them.
+ * Having the whole block be synchronized avoids that race.
+ */
+ synchronized (mInterfaceCache) {
+ if (mInterfaceCache.containsKey(name)) {
+ iface = mInterfaceCache.get(name);
+
+ } else {
+ ILowpanInterface ifaceService = mService.getInterface(name);
+
+ if (ifaceService != null) {
+ iface = getInterface(ifaceService);
+ }
}
- break;
- } catch (RemoteException x) {
- // In all of the cases when we get this exception, we reconnect and try again
- mManager = null;
- manager = getILowpanManager();
}
+ } catch (RemoteException x) {
+ throw x.rethrowFromSystemServer();
}
- return ret;
+ return iface;
}
/**
@@ -160,23 +213,11 @@
*/
@NonNull
public String[] getInterfaceList() {
- ILowpanManager manager = getILowpanManager();
-
- // Maximum number of tries is two. We should only try
- // more than once if our manager has died or there
- // was some sort of AIDL buffer full event.
- for (int i = 0; i < 2 && manager != null; i++) {
- try {
- return manager.getInterfaceList();
- } catch (RemoteException x) {
- // In all of the cases when we get this exception, we reconnect and try again
- mManager = null;
- manager = getILowpanManager();
- }
+ try {
+ return mService.getInterfaceList();
+ } catch (RemoteException x) {
+ throw x.rethrowFromSystemServer();
}
-
- // Return empty list if we have no service.
- return new String[0];
}
/**
@@ -189,55 +230,52 @@
throws LowpanException {
ILowpanManagerListener.Stub listenerBinder =
new ILowpanManagerListener.Stub() {
- public void onInterfaceAdded(ILowpanInterface lowpanInterface) {
- Runnable runnable =
- new Runnable() {
- @Override
- public void run() {
- cb.onInterfaceAdded(
- LowpanInterface.getInterfaceFromBinder(
- lowpanInterface.asBinder()));
- }
- };
+ private Handler mHandler;
+ {
if (handler != null) {
- handler.post(runnable);
+ mHandler = handler;
+ } else if (mLooper != null) {
+ mHandler = new Handler(mLooper);
} else {
- runnable.run();
+ mHandler = new Handler();
}
}
- public void onInterfaceRemoved(ILowpanInterface lowpanInterface) {
+ @Override
+ public void onInterfaceAdded(ILowpanInterface ifaceService) {
Runnable runnable =
- new Runnable() {
- @Override
- public void run() {
- cb.onInterfaceRemoved(
- LowpanInterface.getInterfaceFromBinder(
- lowpanInterface.asBinder()));
+ () -> {
+ LowpanInterface iface = getInterface(ifaceService);
+
+ if (iface != null) {
+ cb.onInterfaceAdded(iface);
}
};
- if (handler != null) {
- handler.post(runnable);
- } else {
- runnable.run();
- }
+ mHandler.post(runnable);
+ }
+
+ @Override
+ public void onInterfaceRemoved(ILowpanInterface ifaceService) {
+ Runnable runnable =
+ () -> {
+ LowpanInterface iface = getInterface(ifaceService);
+
+ if (iface != null) {
+ cb.onInterfaceRemoved(iface);
+ }
+ };
+
+ mHandler.post(runnable);
}
};
- ILowpanManager manager = getILowpanManager();
- if (manager != null) {
- try {
- manager.addListener(listenerBinder);
- } catch (DeadObjectException x) {
- mManager = null;
- // Tickle the ILowpanManager instance, which might
- // get us added back.
- getILowpanManager();
- } catch (Throwable x) {
- LowpanException.throwAsPublicException(x);
- }
+ try {
+ mService.addListener(listenerBinder);
+ } catch (RemoteException x) {
+ throw x.rethrowFromSystemServer();
}
+
synchronized (mListenerMap) {
mListenerMap.put(Integer.valueOf(System.identityHashCode(cb)), listenerBinder);
}
@@ -253,20 +291,23 @@
*
* @hide
*/
- public void unregisterCallback(@NonNull Callback cb) throws AndroidException {
+ public void unregisterCallback(@NonNull Callback cb) {
Integer hashCode = Integer.valueOf(System.identityHashCode(cb));
- ILowpanManagerListener listenerBinder = mListenerMap.get(hashCode);
+ ILowpanManagerListener listenerBinder = null;
+
+ synchronized (mListenerMap) {
+ listenerBinder = mListenerMap.get(hashCode);
+ mListenerMap.remove(hashCode);
+ }
+
if (listenerBinder != null) {
- synchronized (mListenerMap) {
- mListenerMap.remove(hashCode);
+ try {
+ mService.removeListener(listenerBinder);
+ } catch (RemoteException x) {
+ throw x.rethrowFromSystemServer();
}
- if (getILowpanManager() != null) {
- try {
- mManager.removeListener(listenerBinder);
- } catch (DeadObjectException x) {
- mManager = null;
- }
- }
+ } else {
+ throw new RuntimeException("Attempt to unregister an unknown callback");
}
}
}
diff --git a/lowpan/java/android/net/lowpan/LowpanProperties.java b/lowpan/java/android/net/lowpan/LowpanProperties.java
index 0d5acc2..f835260 100644
--- a/lowpan/java/android/net/lowpan/LowpanProperties.java
+++ b/lowpan/java/android/net/lowpan/LowpanProperties.java
@@ -16,9 +16,8 @@
package android.net.lowpan;
+import android.net.IpPrefix;
import android.net.LinkAddress;
-import android.net.RouteInfo;
-import java.util.List;
/** {@hide} */
public final class LowpanProperties {
@@ -77,14 +76,6 @@
public static final LowpanProperty<String> KEY_NCP_VERSION =
new LowpanStandardProperty("android.net.lowpan.property.NCP_VERSION", String.class);
- public static final LowpanProperty<List<LinkAddress>> KEY_LINK_ADDRESS_ARRAY =
- new LowpanStandardProperty(
- "android.net.lowpan.property.LINK_ADDRESS_ARRAY", LinkAddress[].class);
-
- public static final LowpanProperty<List<RouteInfo>> KEY_ROUTE_INFO_ARRAY =
- new LowpanStandardProperty(
- "android.net.lowpan.property.ROUTE_INFO_ARRAY", RouteInfo[].class);
-
/** @hide */
public static final LowpanProperty<byte[]> KEY_EXTENDED_ADDRESS =
new LowpanStandardProperty(
diff --git a/lowpan/java/android/net/lowpan/LowpanScanner.java b/lowpan/java/android/net/lowpan/LowpanScanner.java
index e0df55d9..b0557ee 100644
--- a/lowpan/java/android/net/lowpan/LowpanScanner.java
+++ b/lowpan/java/android/net/lowpan/LowpanScanner.java
@@ -226,8 +226,12 @@
try {
mBinder.startNetScan(map, binderListener);
- } catch (ServiceSpecificException | RemoteException x) {
- LowpanException.throwAsPublicException(x);
+
+ } catch (RemoteException x) {
+ throw x.rethrowAsRuntimeException();
+
+ } catch (ServiceSpecificException x) {
+ throw LowpanException.rethrowAsLowpanException(x);
}
}
@@ -239,8 +243,11 @@
public void stopNetScan() {
try {
mBinder.stopNetScan();
+
} catch (RemoteException x) {
- // Catch and ignore all binder exceptions
+ throw x.rethrowAsRuntimeException();
+
+ } catch (ServiceSpecificException x) {
Log.e(TAG, x.toString());
}
}
@@ -303,10 +310,12 @@
try {
mBinder.startEnergyScan(map, binderListener);
+
} catch (RemoteException x) {
- LowpanException.throwAsPublicException(x);
+ throw x.rethrowAsRuntimeException();
+
} catch (ServiceSpecificException x) {
- LowpanException.throwAsPublicException(x);
+ throw LowpanException.rethrowAsLowpanException(x);
}
}
@@ -318,8 +327,11 @@
public void stopEnergyScan() {
try {
mBinder.stopEnergyScan();
+
} catch (RemoteException x) {
- // Catch and ignore all binder exceptions
+ throw x.rethrowAsRuntimeException();
+
+ } catch (ServiceSpecificException x) {
Log.e(TAG, x.toString());
}
}
diff --git a/lowpan/tests/Android.mk b/lowpan/tests/Android.mk
new file mode 100644
index 0000000..bb0a944
--- /dev/null
+++ b/lowpan/tests/Android.mk
@@ -0,0 +1,65 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+# Make test APK
+# ============================================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+# This list is generated from the java source files in this module
+# The list is a comma separated list of class names with * matching zero or more characters.
+# Example:
+# Input files: src/com/android/server/lowpan/Test.java src/com/android/server/lowpan/AnotherTest.java
+# Generated exclude list: com.android.server.lowpan.Test*,com.android.server.lowpan.AnotherTest*
+
+# Filter all src files to just java files
+local_java_files := $(filter %.java,$(LOCAL_SRC_FILES))
+# Transform java file names into full class names.
+# This only works if the class name matches the file name and the directory structure
+# matches the package.
+local_classes := $(subst /,.,$(patsubst src/%.java,%,$(local_java_files)))
+# Utility variables to allow replacing a space with a comma
+comma:= ,
+empty:=
+space:= $(empty) $(empty)
+# Convert class name list to jacoco exclude list
+# This appends a * to all classes and replace the space separators with commas.
+# These patterns will match all classes in this module and their inner classes.
+jacoco_exclude := $(subst $(space),$(comma),$(patsubst %,%*,$(local_classes)))
+
+jacoco_include := android.net.lowpan.*
+
+LOCAL_JACK_COVERAGE_INCLUDE_FILTER := $(jacoco_include)
+LOCAL_JACK_COVERAGE_EXCLUDE_FILTER := $(jacoco_exclude)
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-test \
+ guava \
+ mockito-target-minus-junit4 \
+ frameworks-base-testutils \
+
+LOCAL_JAVA_LIBRARIES := \
+ android.test.runner \
+
+LOCAL_PACKAGE_NAME := FrameworksLowpanApiTests
+LOCAL_COMPATIBILITY_SUITE := device-tests
+
+LOCAL_CERTIFICATE := platform
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+include $(BUILD_PACKAGE)
diff --git a/lowpan/tests/AndroidManifest.xml b/lowpan/tests/AndroidManifest.xml
new file mode 100644
index 0000000..a216214
--- /dev/null
+++ b/lowpan/tests/AndroidManifest.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ 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
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.net.lowpan.test">
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ <activity android:label="LowpanTestDummyLabel"
+ android:name="LowpanTestDummyName">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ </application>
+
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.net.lowpan.test"
+ android:label="Frameworks LoWPAN API Tests">
+ </instrumentation>
+
+</manifest>
diff --git a/lowpan/tests/AndroidTest.xml b/lowpan/tests/AndroidTest.xml
new file mode 100644
index 0000000..72ad050
--- /dev/null
+++ b/lowpan/tests/AndroidTest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Runs Frameworks LoWPAN API Tests.">
+ <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="test-file-name" value="FrameworksLowpanApiTests.apk" />
+ </target_preparer>
+
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-tag" value="FrameworksLowpanApiTests" />
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="android.net.lowpan.test" />
+ <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+ </test>
+</configuration>
diff --git a/lowpan/tests/README.md b/lowpan/tests/README.md
new file mode 100644
index 0000000..d0eed95
--- /dev/null
+++ b/lowpan/tests/README.md
@@ -0,0 +1,50 @@
+# LoWPAN Unit Tests
+This package contains unit tests for the android LoWPAN framework System APIs based on the
+[Android Testing Support Library](http://developer.android.com/tools/testing-support-library/index.html).
+The test cases are built using the [JUnit](http://junit.org/) and [Mockito](http://mockito.org/)
+libraries.
+
+## Running Tests
+The easiest way to run tests is simply run
+
+```
+frameworks/base/lowpan/tests/runtests.sh
+```
+
+`runtests.sh` will build the test project and all of its dependencies and push the APK to the
+connected device. It will then run the tests on the device.
+
+To pick up changes in framework/base, you will need to:
+1. rebuild the framework library 'make -j32'
+2. sync over the updated library to the device 'adb sync'
+3. restart framework on the device 'adb shell stop' then 'adb shell start'
+
+To enable syncing data to the device for first time after clean reflash:
+1. adb disable-verity
+2. adb reboot
+3. adb remount
+
+See below for a few example of options to limit which tests are run.
+See the
+[AndroidJUnitRunner Documentation](https://developer.android.com/reference/android/support/test/runner/AndroidJUnitRunner.html)
+for more details on the supported options.
+
+```
+runtests.sh -e package android.net.lowpan
+runtests.sh -e class android.net.lowpan.LowpanManagerTest
+```
+
+If you manually build and push the test APK to the device you can run tests using
+
+```
+adb shell am instrument -w 'android.net.wifi.test/android.support.test.runner.AndroidJUnitRunner'
+```
+
+## Adding Tests
+Tests can be added by adding classes to the src directory. JUnit4 style test cases can
+be written by simply annotating test methods with `org.junit.Test`.
+
+## Debugging Tests
+If you are trying to debug why tests are not doing what you expected, you can add android log
+statements and use logcat to view them. The beginning and end of every tests is automatically logged
+with the tag `TestRunner`.
diff --git a/lowpan/tests/runtests.sh b/lowpan/tests/runtests.sh
new file mode 100755
index 0000000..040f4f0
--- /dev/null
+++ b/lowpan/tests/runtests.sh
@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+
+if [ -z $ANDROID_BUILD_TOP ]; then
+ echo "You need to source and lunch before you can use this script"
+ exit 1
+fi
+
+echo "Running tests"
+
+set -e # fail early
+
+echo "+ mmma -j32 $ANDROID_BUILD_TOP/frameworks/base/lowpan/tests"
+# NOTE Don't actually run the command above since this shell doesn't inherit functions from the
+# caller.
+make -j32 -C $ANDROID_BUILD_TOP -f build/core/main.mk MODULES-IN-frameworks-base-lowpan-tests
+
+set -x # print commands
+
+adb root
+adb wait-for-device
+
+adb install -r -g "$OUT/data/app/FrameworksLowpanApiTests/FrameworksLowpanApiTests.apk"
+
+adb shell am instrument -w "$@" 'android.net.lowpan.test/android.support.test.runner.AndroidJUnitRunner'
diff --git a/lowpan/tests/src/android/net/lowpan/LowpanInterfaceTest.java b/lowpan/tests/src/android/net/lowpan/LowpanInterfaceTest.java
new file mode 100644
index 0000000..455ee08
--- /dev/null
+++ b/lowpan/tests/src/android/net/lowpan/LowpanInterfaceTest.java
@@ -0,0 +1,97 @@
+/*
+ * 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 android.net.lowpan;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.*;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.test.TestLooper;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import java.util.Map;
+import java.util.HashMap;
+
+/** Unit tests for android.net.lowpan.LowpanInterface. */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class LowpanInterfaceTest {
+ private static final String TEST_PACKAGE_NAME = "TestPackage";
+
+ @Mock Context mContext;
+ @Mock ILowpanInterface mLowpanInterfaceService;
+ @Mock IBinder mLowpanInterfaceBinder;
+ @Mock ApplicationInfo mApplicationInfo;
+ @Mock IBinder mAppBinder;
+ @Mock LowpanInterface.Callback mLowpanInterfaceCallback;
+
+ private Handler mHandler;
+ private final TestLooper mTestLooper = new TestLooper();
+ private ILowpanInterfaceListener mInterfaceListener;
+ private LowpanInterface mLowpanInterface;
+ private Map<String, Object> mPropertyMap;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ when(mContext.getApplicationInfo()).thenReturn(mApplicationInfo);
+ when(mContext.getOpPackageName()).thenReturn(TEST_PACKAGE_NAME);
+ when(mLowpanInterfaceService.getName()).thenReturn("wpan0");
+ when(mLowpanInterfaceService.asBinder()).thenReturn(mLowpanInterfaceBinder);
+
+ mLowpanInterface = new LowpanInterface(mContext, mLowpanInterfaceService, mTestLooper.getLooper());
+ }
+
+ @Test
+ public void testStateChangedCallback() throws Exception {
+ // Register our callback
+ mLowpanInterface.registerCallback(mLowpanInterfaceCallback);
+
+ // Verify a listener was added
+ verify(mLowpanInterfaceService)
+ .addListener(
+ argThat(
+ listener -> {
+ mInterfaceListener = listener;
+ return listener instanceof ILowpanInterfaceListener;
+ }));
+
+ // Build a changed property map
+ Map<String, Object> changedProperties = new HashMap<>();
+ LowpanProperties.KEY_INTERFACE_STATE.putInMap(changedProperties, LowpanInterface.STATE_OFFLINE);
+
+ // Change some properties
+ mInterfaceListener.onPropertiesChanged(changedProperties);
+ mTestLooper.dispatchAll();
+
+ // Verify that the property was changed
+ verify(mLowpanInterfaceCallback)
+ .onStateChanged(
+ argThat(stateString -> stateString.equals(LowpanInterface.STATE_OFFLINE)));
+ }
+}
diff --git a/lowpan/tests/src/android/net/lowpan/LowpanManagerTest.java b/lowpan/tests/src/android/net/lowpan/LowpanManagerTest.java
new file mode 100644
index 0000000..481ad76
--- /dev/null
+++ b/lowpan/tests/src/android/net/lowpan/LowpanManagerTest.java
@@ -0,0 +1,176 @@
+/*
+ * 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 android.net.lowpan;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.*;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.test.TestLooper;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/** Unit tests for android.net.lowpan.LowpanManager. */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class LowpanManagerTest {
+ private static final String TEST_PACKAGE_NAME = "TestPackage";
+
+ @Mock Context mContext;
+ @Mock ILowpanManager mLowpanService;
+ @Mock ILowpanInterface mLowpanInterfaceService;
+ @Mock IBinder mLowpanInterfaceBinder;
+ @Mock ApplicationInfo mApplicationInfo;
+ @Mock IBinder mAppBinder;
+ @Mock LowpanManager.Callback mLowpanManagerCallback;
+
+ private Handler mHandler;
+ private final TestLooper mTestLooper = new TestLooper();
+ private LowpanManager mLowpanManager;
+
+ private ILowpanManagerListener mManagerListener;
+ private LowpanInterface mLowpanInterface;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ when(mContext.getApplicationInfo()).thenReturn(mApplicationInfo);
+ when(mContext.getOpPackageName()).thenReturn(TEST_PACKAGE_NAME);
+
+ mLowpanManager = new LowpanManager(mContext, mLowpanService, mTestLooper.getLooper());
+ }
+
+ @Test
+ public void testGetEmptyInterfaceList() throws Exception {
+ when(mLowpanService.getInterfaceList()).thenReturn(new String[0]);
+ assertTrue(mLowpanManager.getInterfaceList().length == 0);
+ assertTrue(mLowpanManager.getInterface() == null);
+ }
+
+ @Test
+ public void testGetInterfaceList() throws Exception {
+ when(mLowpanService.getInterfaceList()).thenReturn(new String[] {"wpan0"});
+ when(mLowpanService.getInterface("wpan0")).thenReturn(mLowpanInterfaceService);
+ when(mLowpanInterfaceService.getName()).thenReturn("wpan0");
+ when(mLowpanInterfaceService.asBinder()).thenReturn(mLowpanInterfaceBinder);
+ assertEquals(mLowpanManager.getInterfaceList().length, 1);
+
+ LowpanInterface iface = mLowpanManager.getInterface();
+ assertNotNull(iface);
+ assertEquals(iface.getName(), "wpan0");
+ }
+
+ @Test
+ public void testRegisterCallback() throws Exception {
+ when(mLowpanInterfaceService.getName()).thenReturn("wpan0");
+ when(mLowpanInterfaceService.asBinder()).thenReturn(mLowpanInterfaceBinder);
+
+ // Register our callback
+ mLowpanManager.registerCallback(mLowpanManagerCallback);
+
+ // Verify a listener was added
+ verify(mLowpanService)
+ .addListener(
+ argThat(
+ listener -> {
+ mManagerListener = listener;
+ return listener instanceof ILowpanManagerListener;
+ }));
+
+ // Add an interface
+ mManagerListener.onInterfaceAdded(mLowpanInterfaceService);
+ mTestLooper.dispatchAll();
+
+ // Verify that the interface was added
+ verify(mLowpanManagerCallback)
+ .onInterfaceAdded(
+ argThat(
+ iface -> {
+ mLowpanInterface = iface;
+ return iface instanceof LowpanInterface;
+ }));
+ verifyNoMoreInteractions(mLowpanManagerCallback);
+
+ // This check causes the test to fail with a weird error, but I'm not sure why.
+ assertEquals(mLowpanInterface.getService(), mLowpanInterfaceService);
+
+ // Verify that calling getInterface on the LowpanManager object will yield the same
+ // LowpanInterface object.
+ when(mLowpanService.getInterfaceList()).thenReturn(new String[] {"wpan0"});
+ when(mLowpanService.getInterface("wpan0")).thenReturn(mLowpanInterfaceService);
+ assertEquals(mLowpanManager.getInterface(), mLowpanInterface);
+
+ // Remove the service
+ mManagerListener.onInterfaceRemoved(mLowpanInterfaceService);
+ mTestLooper.dispatchAll();
+
+ // Verify that the interface was removed
+ verify(mLowpanManagerCallback).onInterfaceRemoved(mLowpanInterface);
+ }
+
+ @Test
+ public void testUnregisterCallback() throws Exception {
+ when(mLowpanInterfaceService.getName()).thenReturn("wpan0");
+ when(mLowpanInterfaceService.asBinder()).thenReturn(mLowpanInterfaceBinder);
+
+ // Register our callback
+ mLowpanManager.registerCallback(mLowpanManagerCallback);
+
+ // Verify a listener was added
+ verify(mLowpanService)
+ .addListener(
+ argThat(
+ listener -> {
+ mManagerListener = listener;
+ return listener instanceof ILowpanManagerListener;
+ }));
+
+ // Add an interface
+ mManagerListener.onInterfaceAdded(mLowpanInterfaceService);
+ mTestLooper.dispatchAll();
+
+ // Verify that the interface was added
+ verify(mLowpanManagerCallback)
+ .onInterfaceAdded(
+ argThat(
+ iface -> {
+ mLowpanInterface = iface;
+ return iface instanceof LowpanInterface;
+ }));
+ verifyNoMoreInteractions(mLowpanManagerCallback);
+
+ // Unregister our callback
+ mLowpanManager.unregisterCallback(mLowpanManagerCallback);
+
+ // Verify the listener was removed
+ verify(mLowpanService).removeListener(mManagerListener);
+
+ // Verify that the callback wasn't invoked.
+ verifyNoMoreInteractions(mLowpanManagerCallback);
+ }
+}
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
index c1f03fd..ae8fc80 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
@@ -226,7 +226,8 @@
int httpResponseCode = 500;
int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_PROBE);
try {
- urlConnection = (HttpURLConnection) mNetwork.openConnection(mUrl);
+ urlConnection = (HttpURLConnection) mNetwork.openConnection(
+ new URL(mCm.getCaptivePortalServerUrl()));
urlConnection.setInstanceFollowRedirects(false);
urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS);
urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS);
@@ -234,6 +235,7 @@
urlConnection.getInputStream();
httpResponseCode = urlConnection.getResponseCode();
} catch (IOException e) {
+ loge(e.getMessage());
} finally {
if (urlConnection != null) urlConnection.disconnect();
TrafficStats.setThreadStatsTag(oldTag);
diff --git a/packages/SettingsLib/common.mk b/packages/SettingsLib/common.mk
index 0815534..46b66c4 100644
--- a/packages/SettingsLib/common.mk
+++ b/packages/SettingsLib/common.mk
@@ -20,13 +20,42 @@
SettingsLib
else
LOCAL_RESOURCE_DIR += $(call my-dir)/res
+
+
+## Include transitive dependencies below
+
+# Include support-v7-appcompat, if not already included
+ifeq (,$(findstring android-support-v7-appcompat,$(LOCAL_STATIC_JAVA_LIBRARIES)))
+LOCAL_RESOURCE_DIR += frameworks/support/v7/appcompat/res
+LOCAL_AAPT_FLAGS += --extra-packages android.support.v7.appcompat
+LOCAL_STATIC_JAVA_LIBRARIES += android-support-v7-appcompat
+endif
+
+# Include support-v7-recyclerview, if not already included
+ifeq (,$(findstring android-support-v7-recyclerview,$(LOCAL_STATIC_JAVA_LIBRARIES)))
+LOCAL_RESOURCE_DIR += frameworks/support/v7/recyclerview/res
+LOCAL_AAPT_FLAGS += --extra-packages android.support.v7.recyclerview
+LOCAL_STATIC_JAVA_LIBRARIES += android-support-v7-recyclerview
+endif
+
+# Include android-support-v7-preference, if not already included
+ifeq (,$(findstring android-support-v7-preference,$(LOCAL_STATIC_JAVA_LIBRARIES)))
+LOCAL_RESOURCE_DIR += frameworks/support/v7/preference/res
+LOCAL_AAPT_FLAGS += --extra-packages android.support.v7.preference
+LOCAL_STATIC_JAVA_LIBRARIES += android-support-v7-preference
+endif
+
+# Include android-support-v14-preference, if not already included
+ifeq (,$(findstring android-support-v14-preference,$(LOCAL_STATIC_JAVA_LIBRARIES)))
+LOCAL_RESOURCE_DIR += frameworks/support/v14/preference/res
+LOCAL_AAPT_FLAGS += --extra-packages android.support.v14.preference
+LOCAL_STATIC_JAVA_LIBRARIES += android-support-v14-preference
+endif
+
LOCAL_AAPT_FLAGS += --auto-add-overlay --extra-packages com.android.settingslib
+
LOCAL_STATIC_JAVA_LIBRARIES += \
android-support-annotations \
android-support-v4 \
- android-support-v7-appcompat \
- android-support-v7-preference \
- android-support-v7-recyclerview \
- android-support-v14-preference \
SettingsLib
endif
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index 596eaef..9ccd332 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -16,7 +16,6 @@
package com.android.settingslib.wifi;
import android.annotation.MainThread;
-import android.annotation.Nullable;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -144,7 +143,7 @@
@VisibleForTesting
Scanner mScanner;
- private boolean mStaleScanResults = false;
+ private boolean mStaleScanResults = true;
public WifiTracker(Context context, WifiListener wifiListener,
boolean includeSaved, boolean includeScans) {
@@ -767,20 +766,21 @@
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
- if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action)) {
- mStaleScanResults = false;
- }
-
if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
updateWifiState(intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
WifiManager.WIFI_STATE_UNKNOWN));
- } else if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action) ||
- WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION.equals(action) ||
- WifiManager.LINK_CONFIGURATION_CHANGED_ACTION.equals(action)) {
+ } else if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action)) {
+ mWorkHandler
+ .obtainMessage(
+ WorkHandler.MSG_UPDATE_ACCESS_POINTS,
+ WorkHandler.CLEAR_STALE_SCAN_RESULTS,
+ 0)
+ .sendToTarget();
+ } else if (WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION.equals(action)
+ || WifiManager.LINK_CONFIGURATION_CHANGED_ACTION.equals(action)) {
mWorkHandler.sendEmptyMessage(WorkHandler.MSG_UPDATE_ACCESS_POINTS);
} else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
- NetworkInfo info = (NetworkInfo) intent.getParcelableExtra(
- WifiManager.EXTRA_NETWORK_INFO);
+ NetworkInfo info = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
mConnected.set(info.isConnected());
mMainHandler.sendEmptyMessage(MainHandler.MSG_CONNECTED_CHANGED);
@@ -872,6 +872,8 @@
private static final int MSG_RESUME = 2;
private static final int MSG_UPDATE_WIFI_STATE = 3;
+ private static final int CLEAR_STALE_SCAN_RESULTS = 1;
+
public WorkHandler(Looper looper) {
super(looper);
}
@@ -888,6 +890,9 @@
switch (msg.what) {
case MSG_UPDATE_ACCESS_POINTS:
+ if (msg.arg1 == CLEAR_STALE_SCAN_RESULTS) {
+ mStaleScanResults = false;
+ }
updateAccessPoints();
break;
case MSG_UPDATE_NETWORK_INFO:
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
index 071f921..b6d0c45 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
@@ -724,7 +724,7 @@
verify(mockWifiManager, times(2)).getConfiguredNetworks();
verify(mockConnectivityManager).getNetworkInfo(any(Network.class));
- verify(mockWifiListener).onAccessPointsChanged();
+ verify(mockWifiListener, never()).onAccessPointsChanged(); // mStaleAccessPoints is true
assertThat(tracker.getAccessPoints().size()).isEqualTo(2);
assertThat(tracker.getAccessPoints().get(0).isActive()).isTrue();
}
@@ -798,6 +798,25 @@
}
@Test
+ public void startTrackingShouldNotSendAnyCallbacksUntilScanResultsAreProcessed()
+ throws Exception {
+ WifiTracker tracker = createMockedWifiTracker();
+ startTracking(tracker);
+ waitForHandlersToProcessCurrentlyEnqueuedMessages(tracker);
+
+ tracker.mReceiver.onReceive(mContext, new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION));
+ tracker.mReceiver.onReceive(
+ mContext, new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION));
+ tracker.mReceiver.onReceive(
+ mContext, new Intent(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION));
+
+ waitForHandlersToProcessCurrentlyEnqueuedMessages(tracker);
+ verify(mockWifiListener, never()).onAccessPointsChanged();
+
+ sendScanResultsAndProcess(tracker); // verifies onAccessPointsChanged is invoked
+ }
+
+ @Test
public void disablingWifiShouldClearExistingAccessPoints() throws Exception {
WifiTracker tracker = createTrackerWithScanResultsAndAccessPoint1Connected();
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index bbb461f..b14a17e 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -411,7 +411,8 @@
<!-- started from PipUI -->
<activity
android:name=".pip.tv.PipMenuActivity"
- android:exported="true"
+ android:permission="com.android.systemui.permission.SELF"
+ android:exported="false"
android:theme="@style/PipTheme"
android:launchMode="singleTop"
android:taskAffinity=""
@@ -420,24 +421,10 @@
android:supportsPictureInPicture="true"
androidprv:alwaysFocusable="true"
android:excludeFromRecents="true" />
- <activity
- android:name=".pip.tv.PipOverlayActivity"
- android:exported="true"
- android:theme="@style/PipTheme"
- android:taskAffinity=""
- android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|locale|layoutDirection"
- android:resizeableActivity="true"
- android:supportsPictureInPicture="true"
- android:excludeFromRecents="true" />
- <activity
- android:name=".pip.tv.PipOnboardingActivity"
- android:exported="true"
- android:theme="@style/PipTheme"
- android:launchMode="singleTop"
- android:excludeFromRecents="true" />
<activity
android:name=".pip.phone.PipMenuActivity"
+ android:permission="com.android.systemui.permission.SELF"
android:theme="@style/PipPhoneOverlayControlTheme"
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
android:excludeFromRecents="true"
diff --git a/packages/SystemUI/res/drawable/ic_qs_data_disabled.xml b/packages/SystemUI/res/drawable/ic_qs_data_disabled.xml
index 8b5e4b0..8908975 100644
--- a/packages/SystemUI/res/drawable/ic_qs_data_disabled.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_data_disabled.xml
@@ -14,9 +14,9 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
+ android:width="12dp"
android:height="24.0dp"
- android:viewportWidth="40.0"
+ android:viewportWidth="20.0"
android:viewportHeight="40.0"
android:tint="?android:attr/colorControlNormal">
<path
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_1x.xml b/packages/SystemUI/res/drawable/ic_qs_signal_1x.xml
index 15d521f..5217748 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_1x.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_1x.xml
@@ -14,9 +14,9 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24.0dp"
+ android:width="12.0dp"
android:height="24dp"
- android:viewportWidth="24.0"
+ android:viewportWidth="12.0"
android:viewportHeight="24.0"
android:tint="?android:attr/colorControlNormal">
<path
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_3g.xml b/packages/SystemUI/res/drawable/ic_qs_signal_3g.xml
index ce37331..c84ac8f 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_3g.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_3g.xml
@@ -14,9 +14,9 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
+ android:width="12dp"
android:height="24dp"
- android:viewportWidth="24.0"
+ android:viewportWidth="12.0"
android:viewportHeight="24.0"
android:tint="?android:attr/colorControlNormal">
<path
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_4g.xml b/packages/SystemUI/res/drawable/ic_qs_signal_4g.xml
index 4a8d0ab..26b68c7 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_4g.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_4g.xml
@@ -14,9 +14,9 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24.0dp"
+ android:width="12.0dp"
android:height="24dp"
- android:viewportWidth="24.0"
+ android:viewportWidth="12.0"
android:viewportHeight="24.0"
android:tint="?android:attr/colorControlNormal">
<path
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_4g_plus.xml b/packages/SystemUI/res/drawable/ic_qs_signal_4g_plus.xml
index e0c6b68..6e4b4c5 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_4g_plus.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_4g_plus.xml
@@ -14,9 +14,9 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24.0dp"
- android:height="24.0dp"
- android:viewportWidth="24.0"
+ android:width="15.0dp"
+ android:height="20.0dp"
+ android:viewportWidth="18.0"
android:viewportHeight="24.0"
android:tint="?android:attr/colorControlNormal">
<path
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_e.xml b/packages/SystemUI/res/drawable/ic_qs_signal_e.xml
index 5525508..f4b6ed8 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_e.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_e.xml
@@ -14,9 +14,9 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
+ android:width="12dp"
android:height="24dp"
- android:viewportWidth="24.0"
+ android:viewportWidth="12.0"
android:viewportHeight="24.0"
android:tint="?android:attr/colorControlNormal">
<group
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_g.xml b/packages/SystemUI/res/drawable/ic_qs_signal_g.xml
index f499fe7..60a7f1e9 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_g.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_g.xml
@@ -14,9 +14,9 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
+ android:width="12dp"
android:height="24dp"
- android:viewportWidth="24.0"
+ android:viewportWidth="12.0"
android:viewportHeight="24.0"
android:tint="?android:attr/colorControlNormal">
<group android:translateX="3.5" >
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_h.xml b/packages/SystemUI/res/drawable/ic_qs_signal_h.xml
index 2e6ea23..4ffb4be 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_h.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_h.xml
@@ -14,9 +14,9 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
+ android:width="12dp"
android:height="24dp"
- android:viewportWidth="24.0"
+ android:viewportWidth="12.0"
android:viewportHeight="24.0"
android:tint="?android:attr/colorControlNormal">
<group
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_lte.xml b/packages/SystemUI/res/drawable/ic_qs_signal_lte.xml
index af9c446..816cd32 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_lte.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_lte.xml
@@ -14,9 +14,9 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24.0"
+ android:width="11.9dp"
+ android:height="22dp"
+ android:viewportWidth="13.0"
android:viewportHeight="24.0"
android:tint="?android:attr/colorControlNormal">
<path
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_lte_plus.xml b/packages/SystemUI/res/drawable/ic_qs_signal_lte_plus.xml
index 5ff7d85..4c43e13 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_lte_plus.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_lte_plus.xml
@@ -14,9 +14,9 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24.0dp"
- android:height="24.0dp"
- android:viewportWidth="24.0"
+ android:width="15.0dp"
+ android:height="19.5dp"
+ android:viewportWidth="18.5"
android:viewportHeight="24.0"
android:tint="?android:attr/colorControlNormal">
<path
diff --git a/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_4g_plus.xml b/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_4g_plus.xml
index 3cdd3e1..719a6ca 100644
--- a/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_4g_plus.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_4g_plus.xml
@@ -14,9 +14,9 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="17.0dp"
- android:height="17.0dp"
- android:viewportWidth="24.0"
+ android:width="10.5dp"
+ android:height="14.0dp"
+ android:viewportWidth="18.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FFFFFFFF"
diff --git a/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_lte_plus.xml b/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_lte_plus.xml
index db18fad..62159b3 100644
--- a/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_lte_plus.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_lte_plus.xml
@@ -14,9 +14,9 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="17.0dp"
- android:height="17.0dp"
- android:viewportWidth="24.0"
+ android:width="11.08dp"
+ android:height="14.0dp"
+ android:viewportWidth="19.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FFFFFFFF"
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
index a5ee198..13ba7c1 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
@@ -388,6 +388,11 @@
private void updateFromIntent(Intent intent) {
mToControllerMessenger = intent.getParcelableExtra(EXTRA_CONTROLLER_MESSENGER);
+ if (mToControllerMessenger == null) {
+ Log.w(TAG, "Controller messenger is null. Stopping.");
+ finish();
+ return;
+ }
notifyActivityCallback(mMessenger);
// Register for HidePipMenuEvents once we notify the controller of this activity
@@ -567,6 +572,9 @@
}
private void sendMessage(Message m, String errorMsg) {
+ if (mToControllerMessenger == null) {
+ return;
+ }
try {
mToControllerMessenger.send(m);
} catch (RemoteException e) {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 9588b03..278fdc3 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -30,6 +30,7 @@
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.os.Handler;
import android.os.RemoteException;
import android.util.Log;
@@ -401,7 +402,7 @@
/**
* Updates the appearance of the menu and scrim on top of the PiP while dismissing.
*/
- void updateDismissFraction() {
+ private void updateDismissFraction() {
if (mMenuController != null) {
Rect bounds = mMotionHelper.getBounds();
final float target = mMovementBounds.bottom + bounds.height();
@@ -427,7 +428,7 @@
/**
* Sets the minimized state.
*/
- void setMinimizedStateInternal(boolean isMinimized) {
+ private void setMinimizedStateInternal(boolean isMinimized) {
if (!ENABLE_MINIMIZE) {
return;
}
@@ -466,7 +467,7 @@
/**
* Sets the menu visibility.
*/
- void setMenuState(int menuState, boolean resize) {
+ private void setMenuState(int menuState, boolean resize) {
if (menuState == MENU_STATE_FULL) {
// Save the current snap fraction and if we do not drag or move the PiP, then
// we store back to this snap fraction. Otherwise, we'll reset the snap
@@ -534,7 +535,8 @@
private PipTouchGesture mDefaultMovementGesture = new PipTouchGesture() {
// Whether the PiP was on the left side of the screen at the start of the gesture
private boolean mStartedOnLeft;
- private Point mStartPosition;
+ private final Point mStartPosition = new Point();
+ private final PointF mDelta = new PointF();
@Override
public void onDown(PipTouchState touchState) {
@@ -543,7 +545,8 @@
}
Rect bounds = mMotionHelper.getBounds();
- mStartPosition = new Point(bounds.left, bounds.top);
+ mDelta.set(0f, 0f);
+ mStartPosition.set(bounds.left, bounds.top);
mStartedOnLeft = bounds.left < mMovementBounds.centerX();
mMovementWithinMinimize = true;
mMovementWithinDismiss = touchState.getDownTouchPosition().y >= mMovementBounds.bottom;
@@ -577,10 +580,11 @@
if (touchState.isDragging()) {
// Move the pinned stack freely
- mTmpBounds.set(mMotionHelper.getBounds());
final PointF lastDelta = touchState.getLastTouchDelta();
- float left = mTmpBounds.left + lastDelta.x;
- float top = mTmpBounds.top + lastDelta.y;
+ float lastX = mStartPosition.x + mDelta.x;
+ float lastY = mStartPosition.y + mDelta.y;
+ float left = lastX + lastDelta.x;
+ float top = lastY + lastDelta.y;
if (!touchState.allowDraggingOffscreen() || !ENABLE_MINIMIZE) {
left = Math.max(mMovementBounds.left, Math.min(mMovementBounds.right, left));
}
@@ -590,6 +594,12 @@
} else {
top = Math.max(mMovementBounds.top, Math.min(mMovementBounds.bottom, top));
}
+
+ // Add to the cumulative delta after bounding the position
+ mDelta.x += left - lastX;
+ mDelta.y += top - lastY;
+
+ mTmpBounds.set(mMotionHelper.getBounds());
mTmpBounds.offsetTo((int) left, (int) top);
mMotionHelper.movePip(mTmpBounds);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java
index b2b5b02..dd3361b 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java
@@ -61,7 +61,7 @@
}
/**
- * Processess a given touch event and updates the state.
+ * Processes a given touch event and updates the state.
*/
public void onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java
index 82018ce..e437eff 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java
@@ -45,6 +45,9 @@
@Override
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
+ if (!mPipManager.isPipShown()) {
+ finish();
+ }
setContentView(R.layout.tv_pip_menu);
mPipManager.addListener(this);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 7e656a5..3d66738 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -4587,15 +4587,10 @@
final boolean useDarkText = mColorExtractor.getColors(which, true /* ignoreVisibility */)
.supportsDarkText();
// And wallpaper defines if QS should be light or dark.
- boolean useDarkTheme = false;
- final WallpaperColors systemColors =
- mColorExtractor.getWallpaperColors(WallpaperManager.FLAG_SYSTEM);
- if (systemColors != null) {
- int mainColor = systemColors.getPrimaryColor().toArgb();
- float[] hsl = new float[3];
- ColorUtils.colorToHSL(mainColor, hsl);
- useDarkTheme = hsl[2] < 0.2f;
- }
+ WallpaperColors systemColors = mColorExtractor
+ .getWallpaperColors(WallpaperManager.FLAG_SYSTEM);
+ final boolean useDarkTheme = systemColors != null
+ && (systemColors.getColorHints() & WallpaperColors.HINT_SUPPORTS_DARK_THEME) != 0;
// Enable/disable dark UI.
if (isUsingDarkTheme() != useDarkTheme) {
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 77d3cd3..9486c15 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -2826,11 +2826,11 @@
break;
case FINAL:
- if (!mFinished) finalizeBackup();
- else {
- Slog.e(TAG, "Duplicate finish");
+ if (!mFinished) {
+ finalizeBackup();
+ } else {
+ Slog.e(TAG, "Duplicate finish of K/V pass");
}
- mFinished = true;
break;
}
}
@@ -3184,6 +3184,7 @@
break;
}
}
+ mFinished = true;
Slog.i(BackupManagerService.TAG, "K/V backup pass finished.");
// Only once we're entirely finished do we release the wakelock for k/v backup.
mWakelock.release();
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ec1709a..c8e4b81e 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -21617,7 +21617,8 @@
if (DEBUG_PSS) Slog.d(TAG_PSS,
"Requesting dump heap from "
+ myProc + " to " + heapdumpFile);
- thread.dumpHeap(true, heapdumpFile.toString(), fd);
+ thread.dumpHeap(/* managed=*/ true, /* runGc= */ false,
+ heapdumpFile.toString(), fd);
} catch (RemoteException e) {
}
}
@@ -23348,7 +23349,7 @@
return proc;
}
- public boolean dumpHeap(String process, int userId, boolean managed,
+ public boolean dumpHeap(String process, int userId, boolean managed, boolean runGc,
String path, ParcelFileDescriptor fd) throws RemoteException {
try {
@@ -23377,7 +23378,7 @@
}
}
- proc.thread.dumpHeap(managed, path, fd);
+ proc.thread.dumpHeap(managed, runGc, path, fd);
fd = null;
return true;
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index f0a886d..5d24296 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -785,6 +785,7 @@
final PrintWriter err = getErrPrintWriter();
boolean managed = true;
int userId = UserHandle.USER_CURRENT;
+ boolean runGc = false;
String opt;
while ((opt=getNextOption()) != null) {
@@ -796,6 +797,8 @@
}
} else if (opt.equals("-n")) {
managed = false;
+ } else if (opt.equals("-g")) {
+ runGc = true;
} else {
err.println("Error: Unknown option: " + opt);
return -1;
@@ -811,7 +814,7 @@
return -1;
}
- if (!mInterface.dumpHeap(process, userId, managed, heapFile, fd)) {
+ if (!mInterface.dumpHeap(process, userId, managed, runGc, heapFile, fd)) {
err.println("HEAP DUMP FAILED on process " + process);
return -1;
}
@@ -2555,10 +2558,11 @@
pw.println(" --sampling INTERVAL: use sample profiling with INTERVAL microseconds");
pw.println(" between samples");
pw.println(" --streaming: stream the profiling output to the specified file");
- pw.println(" dumpheap [--user <USER_ID> current] [-n] <PROCESS> <FILE>");
+ pw.println(" dumpheap [--user <USER_ID> current] [-n] [-g] <PROCESS> <FILE>");
pw.println(" Dump the heap of a process. The given <PROCESS> argument may");
pw.println(" be either a process name or pid. Options are:");
pw.println(" -n: dump native heap instead of managed heap");
+ pw.println(" -g: force GC before dumping the heap");
pw.println(" --user <USER_ID> | current: When supplying a process name,");
pw.println(" specify user of process to dump; uses current user if not specified.");
pw.println(" set-debug-app [-w] [--persistent] <PACKAGE>");
diff --git a/services/core/java/com/android/server/notification/AlertRateLimiter.java b/services/core/java/com/android/server/notification/AlertRateLimiter.java
index e4a7934..2b01945 100644
--- a/services/core/java/com/android/server/notification/AlertRateLimiter.java
+++ b/services/core/java/com/android/server/notification/AlertRateLimiter.java
@@ -24,7 +24,7 @@
static final long ALLOWED_ALERT_INTERVAL = 1000;
private long mLastNotificationMillis = 0;
- boolean isRateLimited(long now) {
+ boolean shouldRateLimitAlert(long now) {
final long millisSinceLast = now - mLastNotificationMillis;
if (millisSinceLast < 0 || millisSinceLast < ALLOWED_ALERT_INTERVAL) {
return true;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 48b4c57..c57b2fe 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -20,6 +20,7 @@
import static android.app.NotificationManager.IMPORTANCE_NONE;
import static android.content.pm.PackageManager.FEATURE_LEANBACK;
import static android.content.pm.PackageManager.FEATURE_TELEVISION;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.service.notification.NotificationListenerService
.NOTIFICATION_CHANNEL_OR_GROUP_ADDED;
import static android.service.notification.NotificationListenerService
@@ -210,7 +211,7 @@
= SystemProperties.getBoolean("debug.child_notifs", true);
static final int MAX_PACKAGE_NOTIFICATIONS = 50;
- static final float DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE = 10f;
+ static final float DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE = 5f;
// message codes
static final int MESSAGE_TIMEOUT = 2;
@@ -573,7 +574,8 @@
}
}
- private final NotificationDelegate mNotificationDelegate = new NotificationDelegate() {
+ @VisibleForTesting
+ final NotificationDelegate mNotificationDelegate = new NotificationDelegate() {
@Override
public void onSetDisabled(int status) {
@@ -3358,6 +3360,15 @@
pkg, PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
(userId == UserHandle.USER_ALL) ? UserHandle.USER_SYSTEM : userId);
Notification.addFieldsFromContext(ai, notification);
+
+ int canColorize = mPackageManagerClient.checkPermission(
+ android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, pkg);
+ if (canColorize == PERMISSION_GRANTED) {
+ notification.flags |= Notification.FLAG_CAN_COLORIZE;
+ } else {
+ notification.flags &= ~Notification.FLAG_CAN_COLORIZE;
+ }
+
} catch (NameNotFoundException e) {
Slog.e(TAG, "Cannot create a context for sending app", e);
return;
@@ -3461,8 +3472,19 @@
// package or a registered listener can enqueue. Prevents DOS attacks and deals with leaks.
if (!isSystemNotification && !isNotificationFromListener) {
synchronized (mNotificationLock) {
- if (mNotificationsByKey.get(r.sbn.getKey()) != null) {
- // this is an update, rate limit updates only
+ if (mNotificationsByKey.get(r.sbn.getKey()) == null && isCallerInstantApp(pkg)) {
+ // Ephemeral apps have some special constraints for notifications.
+ // They are not allowed to create new notifications however they are allowed to
+ // update notifications created by the system (e.g. a foreground service
+ // notification).
+ throw new SecurityException("Instant app " + pkg
+ + " cannot create notifications");
+ }
+
+ // rate limit updates that aren't completed progress notifications
+ if (mNotificationsByKey.get(r.sbn.getKey()) != null
+ && !r.getNotification().hasCompletedProgress()) {
+
final float appEnqueueRate = mUsageStats.getAppEnqueueRate(pkg);
if (appEnqueueRate > mMaxPackageEnqueueRate) {
mUsageStats.registerOverRateQuota(pkg);
@@ -3474,33 +3496,15 @@
}
return false;
}
- } else if (isCallerInstantApp(pkg)) {
- // Ephemeral apps have some special constraints for notifications.
- // They are not allowed to create new notifications however they are allowed to
- // update notifications created by the system (e.g. a foreground service
- // notification).
- throw new SecurityException("Instant app " + pkg
- + " cannot create notifications");
}
- int count = 0;
- final int N = mNotificationList.size();
- for (int i=0; i<N; i++) {
- final NotificationRecord existing = mNotificationList.get(i);
- if (existing.sbn.getPackageName().equals(pkg)
- && existing.sbn.getUserId() == userId) {
- if (existing.sbn.getId() == id
- && TextUtils.equals(existing.sbn.getTag(), tag)) {
- break; // Allow updating existing notification
- }
- count++;
- if (count >= MAX_PACKAGE_NOTIFICATIONS) {
- mUsageStats.registerOverCountQuota(pkg);
- Slog.e(TAG, "Package has already posted " + count
- + " notifications. Not showing more. package=" + pkg);
- return false;
- }
- }
+ // limit the number of outstanding notificationrecords an app can have
+ int count = getNotificationCountLocked(pkg, userId, id, tag);
+ if (count >= MAX_PACKAGE_NOTIFICATIONS) {
+ mUsageStats.registerOverCountQuota(pkg);
+ Slog.e(TAG, "Package has already posted or enqueued " + count
+ + " notifications. Not showing more. package=" + pkg);
+ return false;
}
}
}
@@ -3527,6 +3531,32 @@
return true;
}
+ protected int getNotificationCountLocked(String pkg, int userId, int excludedId,
+ String excludedTag) {
+ int count = 0;
+ final int N = mNotificationList.size();
+ for (int i = 0; i < N; i++) {
+ final NotificationRecord existing = mNotificationList.get(i);
+ if (existing.sbn.getPackageName().equals(pkg)
+ && existing.sbn.getUserId() == userId) {
+ if (existing.sbn.getId() == excludedId
+ && TextUtils.equals(existing.sbn.getTag(), excludedTag)) {
+ continue;
+ }
+ count++;
+ }
+ }
+ final int M = mEnqueuedNotifications.size();
+ for (int i = 0; i < M; i++) {
+ final NotificationRecord existing = mEnqueuedNotifications.get(i);
+ if (existing.sbn.getPackageName().equals(pkg)
+ && existing.sbn.getUserId() == userId) {
+ count++;
+ }
+ }
+ return count;
+ }
+
protected boolean isBlocked(NotificationRecord r, NotificationUsageStats usageStats) {
final String pkg = r.sbn.getPackageName();
final int callingUid = r.sbn.getUid();
@@ -3830,7 +3860,8 @@
// notification was a summary and the new one isn't, or when the old
// notification was a summary and its group key changed.
if (oldIsSummary && (!isSummary || !oldGroup.equals(group))) {
- cancelGroupChildrenLocked(old, callingUid, callingPid, null, false /* sendDelete */);
+ cancelGroupChildrenLocked(old, callingUid, callingPid, null, false /* sendDelete */,
+ null);
}
}
@@ -4581,7 +4612,7 @@
boolean wasPosted = removeFromNotificationListsLocked(r);
cancelNotificationLocked(r, sendDelete, reason, wasPosted);
cancelGroupChildrenLocked(r, callingUid, callingPid, listenerName,
- sendDelete);
+ sendDelete, null);
updateLightsLocked();
} else {
// No notification was found, assume that it is snoozed and cancel it.
@@ -4652,7 +4683,6 @@
}
return true;
};
-
cancelAllNotificationsByListLocked(mNotificationList, callingUid, callingPid,
pkg, true /*nullPkgIndicatesUserSwitch*/, channelId, flagChecker,
false /*includeCurrentProfiles*/, userId, false /*sendDelete*/, reason,
@@ -4700,7 +4730,6 @@
if (channelId != null && !channelId.equals(r.getChannel().getId())) {
continue;
}
-
if (canceledNotifications == null) {
canceledNotifications = new ArrayList<>();
}
@@ -4712,7 +4741,7 @@
final int M = canceledNotifications.size();
for (int i = 0; i < M; i++) {
cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
- listenerName, false /* sendDelete */);
+ listenerName, false /* sendDelete */, flagChecker);
}
updateLightsLocked();
}
@@ -4779,7 +4808,7 @@
// Warning: The caller is responsible for invoking updateLightsLocked().
@GuardedBy("mNotificationLock")
private void cancelGroupChildrenLocked(NotificationRecord r, int callingUid, int callingPid,
- String listenerName, boolean sendDelete) {
+ String listenerName, boolean sendDelete, FlagChecker flagChecker) {
Notification n = r.getNotification();
if (!n.isGroupSummary()) {
return;
@@ -4793,15 +4822,15 @@
}
cancelGroupChildrenByListLocked(mNotificationList, r, callingUid, callingPid, listenerName,
- sendDelete, true);
+ sendDelete, true, flagChecker);
cancelGroupChildrenByListLocked(mEnqueuedNotifications, r, callingUid, callingPid,
- listenerName, sendDelete, false);
+ listenerName, sendDelete, false, flagChecker);
}
@GuardedBy("mNotificationLock")
private void cancelGroupChildrenByListLocked(ArrayList<NotificationRecord> notificationList,
NotificationRecord parentNotification, int callingUid, int callingPid,
- String listenerName, boolean sendDelete, boolean wasPosted) {
+ String listenerName, boolean sendDelete, boolean wasPosted, FlagChecker flagChecker) {
final String pkg = parentNotification.sbn.getPackageName();
final int userId = parentNotification.getUserId();
final int reason = REASON_GROUP_SUMMARY_CANCELED;
@@ -4810,7 +4839,8 @@
final StatusBarNotification childSbn = childR.sbn;
if ((childSbn.isGroup() && !childSbn.getNotification().isGroupSummary()) &&
childR.getGroupKey().equals(parentNotification.getGroupKey())
- && (childR.getFlags() & Notification.FLAG_FOREGROUND_SERVICE) == 0) {
+ && (childR.getFlags() & Notification.FLAG_FOREGROUND_SERVICE) == 0
+ && (flagChecker == null || flagChecker.apply(childR.getFlags()))) {
EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, childSbn.getId(),
childSbn.getTag(), userId, 0, 0, reason, listenerName);
notificationList.remove(i);
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 6953ffd..1dee71c 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -386,6 +386,7 @@
prefix = prefix + " ";
pw.println(prefix + "uid=" + sbn.getUid() + " userId=" + sbn.getUserId());
pw.println(prefix + "icon=" + iconStr);
+ pw.println(prefix + "flags=0x" + Integer.toHexString(notification.flags));
pw.println(prefix + "pri=" + notification.priority);
pw.println(prefix + "key=" + sbn.getKey());
pw.println(prefix + "seen=" + mIsSeen);
@@ -495,6 +496,7 @@
pw.println(prefix + "mAttributes= " + mAttributes);
pw.println(prefix + "mLight= " + mLight);
pw.println(prefix + "mShowBadge=" + mShowBadge);
+ pw.println(prefix + "mColorized=" + notification.isColorized());
pw.println(prefix + "effectiveNotificationChannel=" + getChannel());
if (getPeopleOverride() != null) {
pw.println(prefix + "overridePeople= " + TextUtils.join(",", getPeopleOverride()));
@@ -530,10 +532,10 @@
public final String toString() {
return String.format(
"NotificationRecord(0x%08x: pkg=%s user=%s id=%d tag=%s importance=%d key=%s" +
- " channel=%s: %s)",
+ ": %s)",
System.identityHashCode(this),
this.sbn.getPackageName(), this.sbn.getUser(), this.sbn.getId(),
- this.sbn.getTag(), this.mImportance, this.sbn.getKey(), this.getChannel().getId(),
+ this.sbn.getTag(), this.mImportance, this.sbn.getKey(),
this.sbn.getNotification());
}
diff --git a/services/core/java/com/android/server/notification/NotificationUsageStats.java b/services/core/java/com/android/server/notification/NotificationUsageStats.java
index c36a5f2..c8f4d31 100644
--- a/services/core/java/com/android/server/notification/NotificationUsageStats.java
+++ b/services/core/java/com/android/server/notification/NotificationUsageStats.java
@@ -595,7 +595,7 @@
}
public boolean isAlertRateLimited() {
- boolean limited = alertRate.isRateLimited(SystemClock.elapsedRealtime());
+ boolean limited = alertRate.shouldRateLimitAlert(SystemClock.elapsedRealtime());
if (limited) {
numAlertViolations++;
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 5c54ba8..5823771 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -927,7 +927,7 @@
// This is kind of hacky; we're creating a half-parsed package that is
// straddled between the inherited and staged APKs.
final PackageLite pkg = new PackageLite(null, baseApk, null, null, null, null,
- splitPaths.toArray(new String[splitPaths.size()]), null);
+ splitPaths.toArray(new String[splitPaths.size()]), null, null);
final boolean isForwardLocked =
(params.installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0;
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 20d7b28..9765113 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -175,7 +175,7 @@
try {
ApkLite baseApk = PackageParser.parseApkLite(file, 0);
PackageLite pkgLite = new PackageLite(null, baseApk, null, null, null, null,
- null, null);
+ null, null, null);
params.sessionParams.setSize(PackageHelper.calculateInstalledSize(
pkgLite, false, params.sessionParams.abiOverride));
} catch (PackageParserException | IOException e) {
diff --git a/services/core/java/com/android/server/timezone/RulesManagerService.java b/services/core/java/com/android/server/timezone/RulesManagerService.java
index 3d60dcf6..d97ba2d 100644
--- a/services/core/java/com/android/server/timezone/RulesManagerService.java
+++ b/services/core/java/com/android/server/timezone/RulesManagerService.java
@@ -424,8 +424,7 @@
if (stagedDistroRulesVersion == null) {
pw.println("<None>");
} else {
- pw.println("Staged install version: "
- + stagedDistroRulesVersion.toDumpString());
+ pw.println(stagedDistroRulesVersion.toDumpString());
}
break;
case 'a':
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index ed52326..d5e0eb0 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -171,6 +171,8 @@
"com.android.server.wifi.aware.WifiAwareService";
private static final String WIFI_P2P_SERVICE_CLASS =
"com.android.server.wifi.p2p.WifiP2pService";
+ private static final String LOWPAN_SERVICE_CLASS =
+ "com.android.server.lowpan.LowpanService";
private static final String ETHERNET_SERVICE_CLASS =
"com.android.server.ethernet.EthernetService";
private static final String JOB_SCHEDULER_SERVICE_CLASS =
@@ -1089,6 +1091,13 @@
traceEnd();
}
+ if (context.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_LOWPAN)) {
+ traceBeginAndSlog("StartLowpan");
+ mSystemServiceManager.startService(LOWPAN_SERVICE_CLASS);
+ traceEnd();
+ }
+
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_ETHERNET) ||
mPackageManager.hasSystemFeature(PackageManager.FEATURE_USB_HOST)) {
traceBeginAndSlog("StartEthernet");
diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java
index 48fa970..41fccdb 100644
--- a/services/net/java/android/net/ip/IpManager.java
+++ b/services/net/java/android/net/ip/IpManager.java
@@ -1064,7 +1064,7 @@
mNwService.setIPv6AddrGenMode(mInterfaceName, mConfiguration.mIPv6AddrGenMode);
} catch (ServiceSpecificException e) {
if (e.errorCode != OsConstants.EOPNOTSUPP) {
- throw e;
+ logError("Unable to set IPv6 addrgen mode: %s", e);
}
}
}
diff --git a/services/tests/notification/src/com/android/server/notification/AlertRateLimiterTest.java b/services/tests/notification/src/com/android/server/notification/AlertRateLimiterTest.java
index 5ed8210..faf6a9b 100644
--- a/services/tests/notification/src/com/android/server/notification/AlertRateLimiterTest.java
+++ b/services/tests/notification/src/com/android/server/notification/AlertRateLimiterTest.java
@@ -42,31 +42,31 @@
@Test
public void testFirstAlertAllowed() throws Exception {
- assertFalse(mLimiter.isRateLimited(mTestStartTime));
+ assertFalse(mLimiter.shouldRateLimitAlert(mTestStartTime));
}
@Test
public void testAllowedAfterSecond() throws Exception {
- assertFalse(mLimiter.isRateLimited(mTestStartTime));
- assertFalse(mLimiter.isRateLimited(mTestStartTime + ALLOWED_ALERT_INTERVAL));
+ assertFalse(mLimiter.shouldRateLimitAlert(mTestStartTime));
+ assertFalse(mLimiter.shouldRateLimitAlert(mTestStartTime + ALLOWED_ALERT_INTERVAL));
}
@Test
public void testAllowedAfterSecondEvenWithBlockedEntries() throws Exception {
- assertFalse(mLimiter.isRateLimited(mTestStartTime));
- assertTrue(mLimiter.isRateLimited(mTestStartTime + ALLOWED_ALERT_INTERVAL - 1));
- assertFalse(mLimiter.isRateLimited(mTestStartTime + ALLOWED_ALERT_INTERVAL));
+ assertFalse(mLimiter.shouldRateLimitAlert(mTestStartTime));
+ assertTrue(mLimiter.shouldRateLimitAlert(mTestStartTime + ALLOWED_ALERT_INTERVAL - 1));
+ assertFalse(mLimiter.shouldRateLimitAlert(mTestStartTime + ALLOWED_ALERT_INTERVAL));
}
@Test
public void testAllowedDisallowedBeforeSecond() throws Exception {
- assertFalse(mLimiter.isRateLimited(mTestStartTime));
- assertTrue(mLimiter.isRateLimited(mTestStartTime + ALLOWED_ALERT_INTERVAL - 1));
+ assertFalse(mLimiter.shouldRateLimitAlert(mTestStartTime));
+ assertTrue(mLimiter.shouldRateLimitAlert(mTestStartTime + ALLOWED_ALERT_INTERVAL - 1));
}
@Test
public void testDisallowedTimePast() throws Exception {
- assertFalse(mLimiter.isRateLimited(mTestStartTime));
- assertTrue(mLimiter.isRateLimited(mTestStartTime - ALLOWED_ALERT_INTERVAL));
+ assertFalse(mLimiter.shouldRateLimitAlert(mTestStartTime));
+ assertTrue(mLimiter.shouldRateLimitAlert(mTestStartTime - ALLOWED_ALERT_INTERVAL));
}
}
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
index 8ff4639..e799427 100644
--- a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -18,6 +18,7 @@
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_NONE;
+import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
@@ -85,7 +86,6 @@
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
public class NotificationManagerServiceTest extends NotificationTestCase {
- private static final long WAIT_FOR_IDLE_TIMEOUT = 2;
private static final String TEST_CHANNEL_ID = "NotificationManagerServiceTestChannelId";
private final int uid = Binder.getCallingUid();
private NotificationManagerService mNotificationManagerService;
@@ -109,6 +109,7 @@
private AudioManager mAudioManager;
@Mock
ActivityManager mActivityManager;
+
private NotificationChannel mTestNotificationChannel = new NotificationChannel(
TEST_CHANNEL_ID, TEST_CHANNEL_ID, NotificationManager.IMPORTANCE_DEFAULT);
@Mock
@@ -316,7 +317,7 @@
NotificationChannel channel = new NotificationChannel("id", "name",
NotificationManager.IMPORTANCE_HIGH);
- channel.setImportance(NotificationManager.IMPORTANCE_NONE);
+ channel.setImportance(IMPORTANCE_NONE);
NotificationRecord r = generateNotificationRecord(channel);
assertTrue(mNotificationManagerService.isBlocked(r, mUsageStats));
verify(mUsageStats, times(1)).registerBlocked(eq(r));
@@ -483,6 +484,93 @@
}
@Test
+ public void testAppInitiatedCancelAllNotifications_CancelsNoClearFlag() throws Exception {
+ final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
+ sbn.getNotification().flags |= Notification.FLAG_NO_CLEAR;
+ mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+ sbn.getId(), sbn.getNotification(), sbn.getUserId());
+ mBinderService.cancelAllNotifications(PKG, sbn.getUserId());
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(sbn.getPackageName());
+ assertEquals(0, notifs.length);
+ }
+
+ @Test
+ public void testCancelAllNotifications_CancelsNoClearFlag() throws Exception {
+ final NotificationRecord notif = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ notif.getNotification().flags |= Notification.FLAG_NO_CLEAR;
+ mNotificationManagerService.addNotification(notif);
+ mNotificationManagerService.cancelAllNotificationsInt(uid, 0, PKG, null, 0, 0, true,
+ notif.getUserId(), 0, null);
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(notif.sbn.getPackageName());
+ assertEquals(0, notifs.length);
+ }
+
+ @Test
+ public void testUserInitiatedCancelAllOnClearAll_NoClearFlag() throws Exception {
+ final NotificationRecord notif = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ notif.getNotification().flags |= Notification.FLAG_NO_CLEAR;
+ mNotificationManagerService.addNotification(notif);
+
+ mNotificationManagerService.mNotificationDelegate.onClearAll(uid, Binder.getCallingPid(),
+ notif.getUserId());
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(notif.sbn.getPackageName());
+ assertEquals(1, notifs.length);
+ }
+
+ @Test
+ public void testCancelAllCancelNotificationsFromListener_NoClearFlag() throws Exception {
+ final NotificationRecord parent = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ final NotificationRecord child = generateNotificationRecord(
+ mTestNotificationChannel, 2, "group", false);
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, "group", false);
+ child2.getNotification().flags |= Notification.FLAG_NO_CLEAR;
+ final NotificationRecord newGroup = generateNotificationRecord(
+ mTestNotificationChannel, 4, "group2", false);
+ mNotificationManagerService.addNotification(parent);
+ mNotificationManagerService.addNotification(child);
+ mNotificationManagerService.addNotification(child2);
+ mNotificationManagerService.addNotification(newGroup);
+ mNotificationManagerService.getBinderService().cancelNotificationsFromListener(null, null);
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(parent.sbn.getPackageName());
+ assertEquals(1, notifs.length);
+ }
+
+ @Test
+ public void testUserInitiatedCancelAllWithGroup_NoClearFlag() throws Exception {
+ final NotificationRecord parent = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ final NotificationRecord child = generateNotificationRecord(
+ mTestNotificationChannel, 2, "group", false);
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, "group", false);
+ child2.getNotification().flags |= Notification.FLAG_NO_CLEAR;
+ final NotificationRecord newGroup = generateNotificationRecord(
+ mTestNotificationChannel, 4, "group2", false);
+ mNotificationManagerService.addNotification(parent);
+ mNotificationManagerService.addNotification(child);
+ mNotificationManagerService.addNotification(child2);
+ mNotificationManagerService.addNotification(newGroup);
+ mNotificationManagerService.mNotificationDelegate.onClearAll(uid, Binder.getCallingPid(),
+ parent.getUserId());
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(parent.sbn.getPackageName());
+ assertEquals(1, notifs.length);
+ }
+
+ @Test
public void testRemoveForegroundServiceFlag_ImmediatelyAfterEnqueue() throws Exception {
final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
sbn.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
@@ -1119,7 +1207,8 @@
@Test
public void testOnlyAutogroupIfGroupChanged_noGroupChanged_autogroups()
throws Exception {
- NotificationRecord r = generateNotificationRecord(mTestNotificationChannel, 0, "group", false);
+ NotificationRecord r = generateNotificationRecord(mTestNotificationChannel, 0, "group",
+ false);
mNotificationManagerService.addNotification(r);
mNotificationManagerService.addEnqueuedNotification(r);
@@ -1130,4 +1219,60 @@
verify(mGroupHelper, never()).onNotificationPosted(any());
}
+
+ @Test
+ public void testNoFakeColorizedPermission() throws Exception {
+ when(mPackageManagerClient.checkPermission(any(), any())).thenReturn(PERMISSION_DENIED);
+ Notification.Builder nb = new Notification.Builder(mContext,
+ mTestNotificationChannel.getId())
+ .setContentTitle("foo")
+ .setColorized(true)
+ .setFlag(Notification.FLAG_CAN_COLORIZE, true)
+ .setSmallIcon(android.R.drawable.sym_def_app_icon);
+ StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, "tag", uid, 0,
+ nb.build(), new UserHandle(uid), null, 0);
+ NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, null,
+ nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+ waitForIdle();
+
+ NotificationRecord posted = mNotificationManagerService.findNotificationLocked(
+ PKG, null, nr.sbn.getId(), nr.sbn.getUserId());
+
+ assertFalse(posted.getNotification().isColorized());
+ }
+
+ @Test
+ public void testGetNotificationCountLocked() throws Exception {
+ for (int i = 0; i < 20; i++) {
+ NotificationRecord r = generateNotificationRecord(mTestNotificationChannel, i, null, false);
+ mNotificationManagerService.addEnqueuedNotification(r);
+ }
+ for (int i = 0; i < 20; i++) {
+ NotificationRecord r = generateNotificationRecord(mTestNotificationChannel, i, null, false);
+ mNotificationManagerService.addNotification(r);
+ }
+
+ // another package
+ Notification n =
+ new Notification.Builder(mContext, mTestNotificationChannel.getId())
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .build();
+
+ StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "tag", uid, 0,
+ n, new UserHandle(uid), null, 0);
+ NotificationRecord otherPackage =
+ new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+ mNotificationManagerService.addEnqueuedNotification(otherPackage);
+ mNotificationManagerService.addNotification(otherPackage);
+
+ // Same notifications are enqueued as posted, everything counts b/c id and tag don't match
+ assertEquals(40, mNotificationManagerService.getNotificationCountLocked(PKG, new UserHandle(uid).getIdentifier(), 0, null));
+ assertEquals(40, mNotificationManagerService.getNotificationCountLocked(PKG, new UserHandle(uid).getIdentifier(), 0, "tag2"));
+ assertEquals(2, mNotificationManagerService.getNotificationCountLocked("a", new UserHandle(uid).getIdentifier(), 0, "banana"));
+
+ // exclude a known notification - it's excluded from only the posted list, not enqueued
+ assertEquals(39, mNotificationManagerService.getNotificationCountLocked(PKG, new UserHandle(uid).getIdentifier(), 0, "tag"));
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppBoundsTests.java b/services/tests/servicestests/src/com/android/server/wm/AppBoundsTests.java
index a599427..520666b 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppBoundsTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/AppBoundsTests.java
@@ -27,6 +27,7 @@
import android.support.test.runner.AndroidJUnit4;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
/**
@@ -121,7 +122,6 @@
mParentBounds);
}
-
private void testStackBoundsConfiguration(Integer stackId, Rect parentBounds, Rect bounds,
Rect expectedConfigBounds) {
final StackWindowController stackController = stackId != null ?
@@ -140,4 +140,29 @@
assertTrue((expectedConfigBounds == null && config.appBounds == null)
|| expectedConfigBounds.equals(config.appBounds));
}
+
+ /**
+ * Ensures appBounds are considered in {@link Configuration#compareTo(Configuration)}.
+ */
+ @Test
+ public void testConfigurationCompareTo() throws Exception {
+ final Configuration blankConfig = new Configuration();
+
+ final Configuration config1 = new Configuration();
+ config1.appBounds = new Rect(1, 2, 3, 4);
+
+ final Configuration config2 = new Configuration(config1);
+
+ assertEquals(config1.compareTo(config2), 0);
+
+ config2.appBounds.left = 0;
+
+ // Different bounds
+ assertNotEquals(config1.compareTo(config2), 0);
+
+ // No bounds
+ assertEquals(config1.compareTo(blankConfig), -1);
+ assertEquals(blankConfig.compareTo(config1), 1);
+
+ }
}
diff --git a/tests/Internal/src/android/app/WallpaperColorsTest.java b/tests/Internal/src/android/app/WallpaperColorsTest.java
new file mode 100644
index 0000000..5bbd82b
--- /dev/null
+++ b/tests/Internal/src/android/app/WallpaperColorsTest.java
@@ -0,0 +1,80 @@
+/*
+ * 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 android.app;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class WallpaperColorsTest {
+
+ @Test
+ public void supportsDarkTextOverrideTest() {
+ final Color color = Color.valueOf(Color.WHITE);
+ // Default should not support dark text!
+ WallpaperColors colors = new WallpaperColors(color, null, null, 0);
+ Assert.assertTrue("Default behavior is not to support dark text",
+ (colors.getColorHints() & WallpaperColors.HINT_SUPPORTS_DARK_TEXT) == 0);
+
+ // Override it
+ colors = new WallpaperColors(color, null, null, WallpaperColors.HINT_SUPPORTS_DARK_TEXT);
+ Assert.assertFalse("Forcing dark text support doesn't work",
+ (colors.getColorHints() & WallpaperColors.HINT_SUPPORTS_DARK_TEXT) == 0);
+ }
+
+ /**
+ * Sanity check to guarantee that white supports dark text and black doesn't
+ */
+ @Test
+ public void colorHintsTest() {
+ Bitmap image = Bitmap.createBitmap(30, 30, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(image);
+
+ canvas.drawColor(Color.WHITE);
+ int hints = WallpaperColors.fromBitmap(image).getColorHints();
+ boolean supportsDarkText = (hints & WallpaperColors.HINT_SUPPORTS_DARK_TEXT) != 0;
+ boolean supportsDarkTheme = (hints & WallpaperColors.HINT_SUPPORTS_DARK_THEME) != 0;
+ Assert.assertTrue("White surface should support dark text", supportsDarkText);
+ Assert.assertFalse("White surface shouldn't support dark theme", supportsDarkTheme);
+
+ canvas.drawColor(Color.BLACK);
+ hints = WallpaperColors.fromBitmap(image).getColorHints();
+ supportsDarkText = (hints & WallpaperColors.HINT_SUPPORTS_DARK_TEXT) != 0;
+ supportsDarkTheme = (hints & WallpaperColors.HINT_SUPPORTS_DARK_THEME) != 0;
+ Assert.assertFalse("Black surface shouldn't support dark text", supportsDarkText);
+ Assert.assertTrue("Black surface should support dark theme", supportsDarkTheme);
+
+ Paint paint = new Paint();
+ paint.setStyle(Paint.Style.FILL);
+ paint.setColor(Color.BLACK);
+ canvas.drawColor(Color.WHITE);
+ canvas.drawRect(0, 0, 8, 8, paint);
+ supportsDarkText = (WallpaperColors.fromBitmap(image)
+ .getColorHints() & WallpaperColors.HINT_SUPPORTS_DARK_TEXT) != 0;
+ Assert.assertFalse("Light surface shouldn't support dark text "
+ + "when it contains dark pixels", supportsDarkText);
+ }
+}
diff --git a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
index fc68183..8210403 100644
--- a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
+++ b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
@@ -62,16 +62,16 @@
boolean mProgressDone = true;
final int[] kNumberedIconResIDs = {
- R.drawable.notification0,
- R.drawable.notification1,
- R.drawable.notification2,
- R.drawable.notification3,
- R.drawable.notification4,
- R.drawable.notification5,
- R.drawable.notification6,
- R.drawable.notification7,
- R.drawable.notification8,
- R.drawable.notification9
+ R.drawable.notification0,
+ R.drawable.notification1,
+ R.drawable.notification2,
+ R.drawable.notification3,
+ R.drawable.notification4,
+ R.drawable.notification5,
+ R.drawable.notification6,
+ R.drawable.notification7,
+ R.drawable.notification8,
+ R.drawable.notification9
};
final int kUnnumberedIconResID = R.drawable.notificationx;
@@ -438,85 +438,85 @@
mNM.notify("cancel_madness", 7014, n);
}
},
- new Test("Off") {
- public void run() {
- PowerManager pm = (PowerManager) NotificationTestList.this.getSystemService(
- Context.POWER_SERVICE);
- PowerManager.WakeLock wl =
+ new Test("Off") {
+ public void run() {
+ PowerManager pm = (PowerManager) NotificationTestList.this.getSystemService(
+ Context.POWER_SERVICE);
+ PowerManager.WakeLock wl =
pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "sound");
- wl.acquire();
+ wl.acquire();
- pm.goToSleep(SystemClock.uptimeMillis());
+ pm.goToSleep(SystemClock.uptimeMillis());
- Notification n = new Notification.Builder(NotificationTestList.this, "default")
- .setSmallIcon(R.drawable.stat_sys_phone)
- .setContentTitle(name)
- .build();
- Log.d(TAG, "n.sound=" + n.sound);
+ Notification n = new Notification.Builder(NotificationTestList.this, "default")
+ .setSmallIcon(R.drawable.stat_sys_phone)
+ .setContentTitle(name)
+ .build();
+ Log.d(TAG, "n.sound=" + n.sound);
- mNM.notify(1, n);
+ mNM.notify(1, n);
- Log.d(TAG, "releasing wake lock");
- wl.release();
- Log.d(TAG, "released wake lock");
- }
- },
+ Log.d(TAG, "releasing wake lock");
+ wl.release();
+ Log.d(TAG, "released wake lock");
+ }
+ },
- new Test("Cancel #1") {
- public void run()
- {
- mNM.cancel(1);
- }
- },
+ new Test("Cancel #1") {
+ public void run()
+ {
+ mNM.cancel(1);
+ }
+ },
- new Test("Custom Button") {
- public void run() {
- RemoteViews view = new RemoteViews(getPackageName(), R.layout.button_notification);
- view.setOnClickPendingIntent(R.id.button, makeIntent2());
- Notification n = new Notification.Builder(NotificationTestList.this, "default")
- .setSmallIcon(R.drawable.icon1)
- .setWhen(mActivityCreateTime)
- .setContentTitle(name)
- .setOngoing(true)
- .setCustomContentView(view)
- .build();
+ new Test("Custom Button") {
+ public void run() {
+ RemoteViews view = new RemoteViews(getPackageName(), R.layout.button_notification);
+ view.setOnClickPendingIntent(R.id.button, makeIntent2());
+ Notification n = new Notification.Builder(NotificationTestList.this, "default")
+ .setSmallIcon(R.drawable.icon1)
+ .setWhen(mActivityCreateTime)
+ .setContentTitle(name)
+ .setOngoing(true)
+ .setCustomContentView(view)
+ .build();
- mNM.notify(1, n);
- }
- },
+ mNM.notify(1, n);
+ }
+ },
- new Test("Action Button") {
- public void run() {
- Notification n = new Notification.Builder(NotificationTestList.this, "default")
- .setSmallIcon(R.drawable.icon1)
- .setWhen(mActivityCreateTime)
- .setContentTitle(name)
- .setOngoing(true)
- .addAction(new Notification.Action.Builder(
- Icon.createWithResource(NotificationTestList.this,
- R.drawable.ic_statusbar_chat),
- "Button", makeIntent2())
- .build())
- .build();
+ new Test("Action Button") {
+ public void run() {
+ Notification n = new Notification.Builder(NotificationTestList.this, "default")
+ .setSmallIcon(R.drawable.icon1)
+ .setWhen(mActivityCreateTime)
+ .setContentTitle(name)
+ .setOngoing(true)
+ .addAction(new Notification.Action.Builder(
+ Icon.createWithResource(NotificationTestList.this,
+ R.drawable.ic_statusbar_chat),
+ "Button", makeIntent2())
+ .build())
+ .build();
- mNM.notify(1, n);
- }
- },
+ mNM.notify(1, n);
+ }
+ },
- new Test("with intent") {
- public void run() {
- Notification n = new Notification.Builder(NotificationTestList.this, "default")
- .setSmallIcon(R.drawable.icon1)
- .setWhen(mActivityCreateTime)
- .setContentTitle("Persistent #1")
- .setContentText("This is a notification!!!")
- .setContentIntent(makeIntent2())
- .setOngoing(true)
- .build();
+ new Test("with intent") {
+ public void run() {
+ Notification n = new Notification.Builder(NotificationTestList.this, "default")
+ .setSmallIcon(R.drawable.icon1)
+ .setWhen(mActivityCreateTime)
+ .setContentTitle("Persistent #1")
+ .setContentText("This is a notification!!!")
+ .setContentIntent(makeIntent2())
+ .setOngoing(true)
+ .build();
- mNM.notify(1, n);
- }
- },
+ mNM.notify(1, n);
+ }
+ },
new Test("Is blocked?") {
public void run() {
@@ -534,484 +534,484 @@
}
},
- new Test("Whens") {
- public void run()
- {
- Notification.Builder n = new Notification.Builder(
- NotificationTestList.this, "default")
- .setSmallIcon(R.drawable.icon1)
- .setContentTitle(name)
- .setOngoing(true);
+ new Test("Whens") {
+ public void run()
+ {
+ Notification.Builder n = new Notification.Builder(
+ NotificationTestList.this, "default")
+ .setSmallIcon(R.drawable.icon1)
+ .setContentTitle(name)
+ .setOngoing(true);
- mNM.notify(1, n.setContentTitle("(453) 123-2328")
- .setWhen(System.currentTimeMillis()-(1000*60*60*24))
- .build());
+ mNM.notify(1, n.setContentTitle("(453) 123-2328")
+ .setWhen(System.currentTimeMillis()-(1000*60*60*24))
+ .build());
- mNM.notify(1, n.setContentTitle("Mark Willem, Me (2)")
- .setWhen(System.currentTimeMillis())
- .build());
+ mNM.notify(1, n.setContentTitle("Mark Willem, Me (2)")
+ .setWhen(System.currentTimeMillis())
+ .build());
- mNM.notify(1, n.setContentTitle("Sophia Winterlanden")
- .setWhen(System.currentTimeMillis() + (1000 * 60 * 60 * 24))
- .build());
- }
- },
-
- new Test("Bad Icon #1 (when=create)") {
- public void run() {
- Notification n = new Notification.Builder(NotificationTestList.this, "low")
- .setSmallIcon(R.layout.chrono_notification /* not an icon */)
- .setWhen(mActivityCreateTime)
- .setContentTitle("Persistent #1")
- .setContentText("This is the same notification!!")
- .setContentIntent(makeIntent())
- .build();
- mNM.notify(1, n);
- }
- },
-
- new Test("Bad Icon #1 (when=now)") {
- public void run() {
- Notification n = new Notification.Builder(NotificationTestList.this, "low")
- .setSmallIcon(R.layout.chrono_notification /* not an icon */)
- .setWhen(System.currentTimeMillis())
- .setContentTitle("Persistent #1")
- .setContentText("This is the same notification!!")
- .setContentIntent(makeIntent())
- .build();
- mNM.notify(1, n);
- }
- },
-
- new Test("Null Icon #1 (when=now)") {
- public void run() {
- Notification n = new Notification.Builder(NotificationTestList.this, "low")
- .setSmallIcon(0)
- .setWhen(System.currentTimeMillis())
- .setContentTitle("Persistent #1")
- .setContentText("This is the same notification!!")
- .setContentIntent(makeIntent())
- .build();
- mNM.notify(1, n);
- }
- },
-
- new Test("Bad resource #1 (when=create)") {
- public void run() {
- Notification n = new Notification.Builder(NotificationTestList.this, "low")
- .setSmallIcon(R.drawable.icon2)
- .setWhen(mActivityCreateTime)
- .setContentTitle("Persistent #1")
- .setContentText("This is the same notification!!")
- .setContentIntent(makeIntent())
- .build();
- n.contentView.setInt(1 /*bogus*/, "bogus method", 666);
- mNM.notify(1, n);
- }
- },
-
- new Test("Bad resource #1 (when=now)") {
- public void run() {
- Notification n = new Notification.Builder(NotificationTestList.this, "low")
- .setSmallIcon(R.drawable.icon2)
- .setWhen(System.currentTimeMillis())
- .setContentTitle("Persistent #1")
- .setContentText("This is the same notification!!")
- .setContentIntent(makeIntent())
- .build();
- n.contentView.setInt(1 /*bogus*/, "bogus method", 666);
- mNM.notify(1, n);
- }
- },
-
- new Test("Times") {
- public void run()
- {
- long now = System.currentTimeMillis();
-
- timeNotification(7, "24 hours from now", now+(1000*60*60*24));
- timeNotification(6, "12:01:00 from now", now+(1000*60*60*12)+(60*1000));
- timeNotification(5, "12 hours from now", now+(1000*60*60*12));
- timeNotification(4, "now", now);
- timeNotification(3, "11:59:00 ago", now-((1000*60*60*12)-(60*1000)));
- timeNotification(2, "12 hours ago", now-(1000*60*60*12));
- timeNotification(1, "24 hours ago", now-(1000*60*60*24));
- }
- },
- new StateStress("Stress - Ongoing / Latest", 100, 100, new Runnable[] {
- new Runnable() {
- public void run() {
- Log.d(TAG, "Stress - Ongoing/Latest 0");
- Notification n = new Notification.Builder(NotificationTestList.this, "low")
- .setSmallIcon(R.drawable.icon3)
- .setWhen(System.currentTimeMillis())
- .setContentTitle("Stress - Ongoing")
- .setContentText("Notify me!!!")
- .setOngoing(true)
- .build();
- mNM.notify(1, n);
- }
- },
- new Runnable() {
- public void run() {
- Log.d(TAG, "Stress - Ongoing/Latest 1");
- Notification n = new Notification.Builder(NotificationTestList.this, "low")
- .setSmallIcon(R.drawable.icon4)
- .setWhen(System.currentTimeMillis())
- .setContentTitle("Stress - Latest")
- .setContentText("Notify me!!!")
- .build();
- mNM.notify(1, n);
- }
+ mNM.notify(1, n.setContentTitle("Sophia Winterlanden")
+ .setWhen(System.currentTimeMillis() + (1000 * 60 * 60 * 24))
+ .build());
}
- }),
+ },
- new Test("Long") {
- public void run()
- {
- NotificationChannel channel = new NotificationChannel("v. noisy",
- "channel for sound and a custom vibration", IMPORTANCE_DEFAULT);
- channel.enableVibration(true);
- channel.setVibrationPattern(new long[] {
- 300, 400, 300, 400, 300, 400, 300, 400, 300, 400, 300, 400,
- 300, 400, 300, 400, 300, 400, 300, 400, 300, 400, 300, 400,
- 300, 400, 300, 400, 300, 400, 300, 400, 300, 400, 300, 400 });
- mNM.createNotificationChannel(channel);
+ new Test("Bad Icon #1 (when=create)") {
+ public void run() {
+ Notification n = new Notification.Builder(NotificationTestList.this, "low")
+ .setSmallIcon(R.layout.chrono_notification /* not an icon */)
+ .setWhen(mActivityCreateTime)
+ .setContentTitle("Persistent #1")
+ .setContentText("This is the same notification!!")
+ .setContentIntent(makeIntent())
+ .build();
+ mNM.notify(1, n);
+ }
+ },
- Notification n = new Notification.Builder(NotificationTestList.this, "v. noisy")
- .setSmallIcon(R.drawable.icon1)
- .setContentTitle(name)
- .build();
- mNM.notify(1, n);
- }
- },
+ new Test("Bad Icon #1 (when=now)") {
+ public void run() {
+ Notification n = new Notification.Builder(NotificationTestList.this, "low")
+ .setSmallIcon(R.layout.chrono_notification /* not an icon */)
+ .setWhen(System.currentTimeMillis())
+ .setContentTitle("Persistent #1")
+ .setContentText("This is the same notification!!")
+ .setContentIntent(makeIntent())
+ .build();
+ mNM.notify(1, n);
+ }
+ },
- new Test("Progress #1") {
- public void run() {
- final boolean PROGRESS_UPDATES_WHEN = true;
- if (!mProgressDone) return;
- mProgressDone = false;
- Thread t = new Thread() {
- public void run() {
- int x = 0;
- final Notification.Builder n = new Notification.Builder(
- NotificationTestList.this, "low")
- .setSmallIcon(R.drawable.icon1)
- .setContentTitle(name)
- .setOngoing(true);
+ new Test("Null Icon #1 (when=now)") {
+ public void run() {
+ Notification n = new Notification.Builder(NotificationTestList.this, "low")
+ .setSmallIcon(0)
+ .setWhen(System.currentTimeMillis())
+ .setContentTitle("Persistent #1")
+ .setContentText("This is the same notification!!")
+ .setContentIntent(makeIntent())
+ .build();
+ mNM.notify(1, n);
+ }
+ },
- while (!mProgressDone) {
- n.setWhen(PROGRESS_UPDATES_WHEN
- ? System.currentTimeMillis()
- : mActivityCreateTime);
- n.setProgress(100, x, false);
- n.setContentText("Progress: " + x + "%");
+ new Test("Bad resource #1 (when=create)") {
+ public void run() {
+ Notification n = new Notification.Builder(NotificationTestList.this, "low")
+ .setSmallIcon(R.drawable.icon2)
+ .setWhen(mActivityCreateTime)
+ .setContentTitle("Persistent #1")
+ .setContentText("This is the same notification!!")
+ .setContentIntent(makeIntent())
+ .build();
+ n.contentView.setInt(1 /*bogus*/, "bogus method", 666);
+ mNM.notify(1, n);
+ }
+ },
- mNM.notify(500, n.build());
- x = (x + 7) % 100;
+ new Test("Bad resource #1 (when=now)") {
+ public void run() {
+ Notification n = new Notification.Builder(NotificationTestList.this, "low")
+ .setSmallIcon(R.drawable.icon2)
+ .setWhen(System.currentTimeMillis())
+ .setContentTitle("Persistent #1")
+ .setContentText("This is the same notification!!")
+ .setContentIntent(makeIntent())
+ .build();
+ n.contentView.setInt(1 /*bogus*/, "bogus method", 666);
+ mNM.notify(1, n);
+ }
+ },
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- break;
- }
+ new Test("Times") {
+ public void run()
+ {
+ long now = System.currentTimeMillis();
+
+ timeNotification(7, "24 hours from now", now+(1000*60*60*24));
+ timeNotification(6, "12:01:00 from now", now+(1000*60*60*12)+(60*1000));
+ timeNotification(5, "12 hours from now", now+(1000*60*60*12));
+ timeNotification(4, "now", now);
+ timeNotification(3, "11:59:00 ago", now-((1000*60*60*12)-(60*1000)));
+ timeNotification(2, "12 hours ago", now-(1000*60*60*12));
+ timeNotification(1, "24 hours ago", now-(1000*60*60*24));
+ }
+ },
+ new StateStress("Stress - Ongoing / Latest", 100, 100, new Runnable[] {
+ new Runnable() {
+ public void run() {
+ Log.d(TAG, "Stress - Ongoing/Latest 0");
+ Notification n = new Notification.Builder(NotificationTestList.this, "low")
+ .setSmallIcon(R.drawable.icon3)
+ .setWhen(System.currentTimeMillis())
+ .setContentTitle("Stress - Ongoing")
+ .setContentText("Notify me!!!")
+ .setOngoing(true)
+ .build();
+ mNM.notify(1, n);
+ }
+ },
+ new Runnable() {
+ public void run() {
+ Log.d(TAG, "Stress - Ongoing/Latest 1");
+ Notification n = new Notification.Builder(NotificationTestList.this, "low")
+ .setSmallIcon(R.drawable.icon4)
+ .setWhen(System.currentTimeMillis())
+ .setContentTitle("Stress - Latest")
+ .setContentText("Notify me!!!")
+ .build();
+ mNM.notify(1, n);
}
}
- };
- t.start();
- }
- },
+ }),
- new Test("Stop Progress") {
- public void run() {
- mProgressDone = true;
- mNM.cancel(500);
- }
- },
+ new Test("Long") {
+ public void run()
+ {
+ NotificationChannel channel = new NotificationChannel("v. noisy",
+ "channel for sound and a custom vibration", IMPORTANCE_DEFAULT);
+ channel.enableVibration(true);
+ channel.setVibrationPattern(new long[] {
+ 300, 400, 300, 400, 300, 400, 300, 400, 300, 400, 300, 400,
+ 300, 400, 300, 400, 300, 400, 300, 400, 300, 400, 300, 400,
+ 300, 400, 300, 400, 300, 400, 300, 400, 300, 400, 300, 400 });
+ mNM.createNotificationChannel(channel);
- new Test("Blue Lights") {
- public void run()
- {
- NotificationChannel channel = new NotificationChannel("blue",
- "blue", IMPORTANCE_DEFAULT);
- channel.enableLights(true);
- channel.setLightColor(0xff0000ff);
- mNM.createNotificationChannel(channel);
+ Notification n = new Notification.Builder(NotificationTestList.this, "v. noisy")
+ .setSmallIcon(R.drawable.icon1)
+ .setContentTitle(name)
+ .build();
+ mNM.notify(1, n);
+ }
+ },
- Notification n = new Notification.Builder(NotificationTestList.this, "blue")
- .setSmallIcon(R.drawable.icon2)
- .setContentTitle(name)
- .build();
- mNM.notify(1, n);
- }
- },
+ new Test("Progress #1") {
+ public void run() {
+ final boolean PROGRESS_UPDATES_WHEN = true;
+ if (!mProgressDone) return;
+ mProgressDone = false;
+ Thread t = new Thread() {
+ public void run() {
+ int x = 0;
+ final Notification.Builder n = new Notification.Builder(
+ NotificationTestList.this, "low")
+ .setSmallIcon(R.drawable.icon1)
+ .setContentTitle(name)
+ .setOngoing(true);
- new Test("Red Lights") {
- public void run()
- {
- NotificationChannel channel = new NotificationChannel("red",
- "red", IMPORTANCE_DEFAULT);
- channel.enableLights(true);
- channel.setLightColor(0xffff0000);
- mNM.createNotificationChannel(channel);
+ while (!mProgressDone) {
+ n.setWhen(PROGRESS_UPDATES_WHEN
+ ? System.currentTimeMillis()
+ : mActivityCreateTime);
+ n.setProgress(100, x, false);
+ n.setContentText("Progress: " + x + "%");
- Notification n = new Notification.Builder(NotificationTestList.this, "red")
- .setSmallIcon(R.drawable.icon2)
- .setContentTitle(name)
- .build();
- mNM.notify(1, n);
- }
- },
+ mNM.notify(500, n.build());
+ x = (x + 7) % 100;
- new Test("Lights off") {
- public void run()
- {
- Notification n = new Notification.Builder(NotificationTestList.this, "default")
- .setSmallIcon(R.drawable.icon2)
- .setContentTitle(name)
- .build();
- mNM.notify(1, n);
- }
- },
-
- new Test("Alert once") {
- public void run()
- {
- Notification n = new Notification.Builder(NotificationTestList.this, "high")
- .setSmallIcon(R.drawable.icon2)
- .setContentTitle(name)
- .setOnlyAlertOnce(true)
- .build();
- mNM.notify(1, n);
- }
- },
-
- new Test("Resource Sound") {
- public void run()
- {
- NotificationChannel channel = new NotificationChannel("res_sound",
- "resource sound", IMPORTANCE_DEFAULT);
- channel.setSound(Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" +
- getPackageName() + "/raw/ringer"), Notification.AUDIO_ATTRIBUTES_DEFAULT);
- mNM.createNotificationChannel(channel);
-
- Notification n = new Notification.Builder(NotificationTestList.this, "res_sound")
- .setSmallIcon(R.drawable.stat_sys_phone)
- .setContentTitle(name)
- .build();
- Log.d(TAG, "n.sound=" + n.sound);
-
- mNM.notify(1, n);
- }
- },
-
- new Test("Sound and Cancel") {
- public void run()
- {
- NotificationChannel channel = new NotificationChannel("res_sound",
- "resource sound", IMPORTANCE_DEFAULT);
- channel.setSound(Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" +
- getPackageName() + "/raw/ringer"), Notification.AUDIO_ATTRIBUTES_DEFAULT);
- mNM.createNotificationChannel(channel);
-
- Notification n = new Notification.Builder(NotificationTestList.this, "res_sound")
- .setSmallIcon(R.drawable.stat_sys_phone)
- .setContentTitle(name)
- .build();
-
- mNM.notify(1, n);
- SystemClock.sleep(600);
- mNM.cancel(1);
- }
- },
-
- new Test("Vibrate and cancel") {
- public void run()
- {
- NotificationChannel channel = new NotificationChannel("vibrate",
- "vibrate", IMPORTANCE_DEFAULT);
- channel.enableVibration(true);
- channel.setVibrationPattern(new long[] {0, 700, 500, 1000, 0, 700, 500, 1000,
- 0, 700, 500, 1000, 0, 700, 500, 1000, 0, 700, 500, 1000, 0, 700, 500, 1000,
- 0, 700, 500, 1000, 0, 700, 500, 1000});
- mNM.createNotificationChannel(channel);
-
- Notification n = new Notification.Builder(NotificationTestList.this, "vibrate")
- .setSmallIcon(R.drawable.stat_sys_phone)
- .setContentTitle(name)
- .build();
-
- mNM.notify(1, n);
- SystemClock.sleep(500);
- mNM.cancel(1);
- }
- },
-
- new Test("Vibrate pattern") {
- public void run()
- {
- mVibrator.vibrate(new long[] { 250, 1000, 500, 2000 }, -1);
- }
- },
-
- new Test("Vibrate pattern repeating") {
- public void run()
- {
- mVibrator.vibrate(new long[] { 250, 1000, 500 }, 1);
- }
- },
-
- new Test("Vibrate 3s") {
- public void run()
- {
- mVibrator.vibrate(3000);
- }
- },
-
- new Test("Vibrate 100s") {
- public void run()
- {
- mVibrator.vibrate(100000);
- }
- },
-
- new Test("Vibrate off") {
- public void run()
- {
- mVibrator.cancel();
- }
- },
-
- new Test("Cancel #1") {
- public void run() {
- mNM.cancel(1);
- }
- },
-
- new Test("Cancel #1 in 3 sec") {
- public void run() {
- mHandler.postDelayed(new Runnable() {
- public void run() {
- Log.d(TAG, "Cancelling now...");
- mNM.cancel(1);
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ break;
+ }
}
- }, 3000);
- }
- },
+ }
+ };
+ t.start();
+ }
+ },
- new Test("Cancel #2") {
- public void run() {
- mNM.cancel(2);
- }
- },
+ new Test("Stop Progress") {
+ public void run() {
+ mProgressDone = true;
+ mNM.cancel(500);
+ }
+ },
- new Test("Persistent #1") {
- public void run() {
- Notification n = new Notification.Builder(NotificationTestList.this)
- .setSmallIcon(R.drawable.icon1)
- .setWhen(mActivityCreateTime)
- .setContentTitle(name)
- .setContentText("This is a notification!!!")
- .setContentIntent(makeIntent())
- .build();
- mNM.notify(1, n);
- }
- },
+ new Test("Blue Lights") {
+ public void run()
+ {
+ NotificationChannel channel = new NotificationChannel("blue",
+ "blue", IMPORTANCE_DEFAULT);
+ channel.enableLights(true);
+ channel.setLightColor(0xff0000ff);
+ mNM.createNotificationChannel(channel);
- new Test("Persistent #1 in 3 sec") {
- public void run() {
- mHandler.postDelayed(new Runnable() {
- public void run() {
- String message = " "
- + "tick tock tick tock\n\nSometimes notifications can "
- + "be really long and wrap to more than one line.\n"
- + "Sometimes."
- + "Ohandwhathappensifwehaveonereallylongstringarewesure"
- + "thatwesegmentitcorrectly?\n";
- Notification n = new Notification.Builder(
- NotificationTestList.this, "low")
- .setSmallIcon(R.drawable.icon1)
- .setContentTitle(name)
- .setContentText("This is still a notification!!!")
- .setContentIntent(makeIntent())
- .setStyle(new Notification.BigTextStyle().bigText(message))
- .build();
- mNM.notify(1, n);
- }
- }, 3000);
- }
- },
+ Notification n = new Notification.Builder(NotificationTestList.this, "blue")
+ .setSmallIcon(R.drawable.icon2)
+ .setContentTitle(name)
+ .build();
+ mNM.notify(1, n);
+ }
+ },
- new Test("Persistent #2") {
- public void run() {
- Notification n = new Notification.Builder(NotificationTestList.this, "low")
- .setSmallIcon(R.drawable.icon1)
- .setWhen(mActivityCreateTime)
- .setContentTitle(name)
- .setContentText("This is a notification!!!")
- .setContentIntent(makeIntent())
- .build();
- mNM.notify(2, n);
- }
- },
+ new Test("Red Lights") {
+ public void run()
+ {
+ NotificationChannel channel = new NotificationChannel("red",
+ "red", IMPORTANCE_DEFAULT);
+ channel.enableLights(true);
+ channel.setLightColor(0xffff0000);
+ mNM.createNotificationChannel(channel);
- new Test("Persistent #3") {
- public void run() {
- Notification n = new Notification.Builder(NotificationTestList.this, "low")
- .setSmallIcon(R.drawable.icon1)
- .setWhen(mActivityCreateTime)
- .setContentTitle(name)
- .setContentText("This is a notification!!!")
- .setContentIntent(makeIntent())
- .build();
- mNM.notify(3, n);
- }
- },
+ Notification n = new Notification.Builder(NotificationTestList.this, "red")
+ .setSmallIcon(R.drawable.icon2)
+ .setContentTitle(name)
+ .build();
+ mNM.notify(1, n);
+ }
+ },
- new Test("Persistent #2 Vibrate") {
- public void run() {
- Notification n = new Notification.Builder(NotificationTestList.this, "low")
- .setSmallIcon(R.drawable.icon1)
- .setWhen(mActivityCreateTime)
- .setContentTitle(name)
- .setContentText("This is a notification!!!")
- .setContentIntent(makeIntent())
- .setDefaults(Notification.DEFAULT_VIBRATE)
- .build();
- mNM.notify(2, n);
- }
- },
+ new Test("Lights off") {
+ public void run()
+ {
+ Notification n = new Notification.Builder(NotificationTestList.this, "default")
+ .setSmallIcon(R.drawable.icon2)
+ .setContentTitle(name)
+ .build();
+ mNM.notify(1, n);
+ }
+ },
- new Test("Persistent #1 - different icon") {
- public void run() {
- Notification n = new Notification.Builder(NotificationTestList.this, "low")
- .setSmallIcon(R.drawable.icon2)
- .setWhen(mActivityCreateTime)
- .setContentTitle(name)
- .setContentText("This is a notification!!!")
- .setContentIntent(makeIntent())
- .build();
- mNM.notify(1, n);
- }
- },
+ new Test("Alert once") {
+ public void run()
+ {
+ Notification n = new Notification.Builder(NotificationTestList.this, "high")
+ .setSmallIcon(R.drawable.icon2)
+ .setContentTitle(name)
+ .setOnlyAlertOnce(true)
+ .build();
+ mNM.notify(1, n);
+ }
+ },
- new Test("Chronometer Start") {
- public void run() {
- Notification n = new Notification.Builder(NotificationTestList.this, "low")
- .setSmallIcon(R.drawable.icon1)
- .setWhen(System.currentTimeMillis())
- .setContentTitle(name)
- .setContentIntent(makeIntent())
- .setOngoing(true)
- .setUsesChronometer(true)
- .build();
- mNM.notify(2, n);
- }
- },
+ new Test("Resource Sound") {
+ public void run()
+ {
+ NotificationChannel channel = new NotificationChannel("res_sound",
+ "resource sound", IMPORTANCE_DEFAULT);
+ channel.setSound(Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" +
+ getPackageName() + "/raw/ringer"), Notification.AUDIO_ATTRIBUTES_DEFAULT);
+ mNM.createNotificationChannel(channel);
- new Test("Chronometer Stop") {
- public void run() {
- mHandler.postDelayed(new Runnable() {
+ Notification n = new Notification.Builder(NotificationTestList.this, "res_sound")
+ .setSmallIcon(R.drawable.stat_sys_phone)
+ .setContentTitle(name)
+ .build();
+ Log.d(TAG, "n.sound=" + n.sound);
+
+ mNM.notify(1, n);
+ }
+ },
+
+ new Test("Sound and Cancel") {
+ public void run()
+ {
+ NotificationChannel channel = new NotificationChannel("res_sound",
+ "resource sound", IMPORTANCE_DEFAULT);
+ channel.setSound(Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" +
+ getPackageName() + "/raw/ringer"), Notification.AUDIO_ATTRIBUTES_DEFAULT);
+ mNM.createNotificationChannel(channel);
+
+ Notification n = new Notification.Builder(NotificationTestList.this, "res_sound")
+ .setSmallIcon(R.drawable.stat_sys_phone)
+ .setContentTitle(name)
+ .build();
+
+ mNM.notify(1, n);
+ SystemClock.sleep(600);
+ mNM.cancel(1);
+ }
+ },
+
+ new Test("Vibrate and cancel") {
+ public void run()
+ {
+ NotificationChannel channel = new NotificationChannel("vibrate",
+ "vibrate", IMPORTANCE_DEFAULT);
+ channel.enableVibration(true);
+ channel.setVibrationPattern(new long[] {0, 700, 500, 1000, 0, 700, 500, 1000,
+ 0, 700, 500, 1000, 0, 700, 500, 1000, 0, 700, 500, 1000, 0, 700, 500, 1000,
+ 0, 700, 500, 1000, 0, 700, 500, 1000});
+ mNM.createNotificationChannel(channel);
+
+ Notification n = new Notification.Builder(NotificationTestList.this, "vibrate")
+ .setSmallIcon(R.drawable.stat_sys_phone)
+ .setContentTitle(name)
+ .build();
+
+ mNM.notify(1, n);
+ SystemClock.sleep(500);
+ mNM.cancel(1);
+ }
+ },
+
+ new Test("Vibrate pattern") {
+ public void run()
+ {
+ mVibrator.vibrate(new long[] { 250, 1000, 500, 2000 }, -1);
+ }
+ },
+
+ new Test("Vibrate pattern repeating") {
+ public void run()
+ {
+ mVibrator.vibrate(new long[] { 250, 1000, 500 }, 1);
+ }
+ },
+
+ new Test("Vibrate 3s") {
+ public void run()
+ {
+ mVibrator.vibrate(3000);
+ }
+ },
+
+ new Test("Vibrate 100s") {
+ public void run()
+ {
+ mVibrator.vibrate(100000);
+ }
+ },
+
+ new Test("Vibrate off") {
+ public void run()
+ {
+ mVibrator.cancel();
+ }
+ },
+
+ new Test("Cancel #1") {
+ public void run() {
+ mNM.cancel(1);
+ }
+ },
+
+ new Test("Cancel #1 in 3 sec") {
+ public void run() {
+ mHandler.postDelayed(new Runnable() {
+ public void run() {
+ Log.d(TAG, "Cancelling now...");
+ mNM.cancel(1);
+ }
+ }, 3000);
+ }
+ },
+
+ new Test("Cancel #2") {
+ public void run() {
+ mNM.cancel(2);
+ }
+ },
+
+ new Test("Persistent #1") {
+ public void run() {
+ Notification n = new Notification.Builder(NotificationTestList.this)
+ .setSmallIcon(R.drawable.icon1)
+ .setWhen(mActivityCreateTime)
+ .setContentTitle(name)
+ .setContentText("This is a notification!!!")
+ .setContentIntent(makeIntent())
+ .build();
+ mNM.notify(1, n);
+ }
+ },
+
+ new Test("Persistent #1 in 3 sec") {
+ public void run() {
+ mHandler.postDelayed(new Runnable() {
+ public void run() {
+ String message = " "
+ + "tick tock tick tock\n\nSometimes notifications can "
+ + "be really long and wrap to more than one line.\n"
+ + "Sometimes."
+ + "Ohandwhathappensifwehaveonereallylongstringarewesure"
+ + "thatwesegmentitcorrectly?\n";
+ Notification n = new Notification.Builder(
+ NotificationTestList.this, "low")
+ .setSmallIcon(R.drawable.icon1)
+ .setContentTitle(name)
+ .setContentText("This is still a notification!!!")
+ .setContentIntent(makeIntent())
+ .setStyle(new Notification.BigTextStyle().bigText(message))
+ .build();
+ mNM.notify(1, n);
+ }
+ }, 3000);
+ }
+ },
+
+ new Test("Persistent #2") {
+ public void run() {
+ Notification n = new Notification.Builder(NotificationTestList.this, "low")
+ .setSmallIcon(R.drawable.icon1)
+ .setWhen(mActivityCreateTime)
+ .setContentTitle(name)
+ .setContentText("This is a notification!!!")
+ .setContentIntent(makeIntent())
+ .build();
+ mNM.notify(2, n);
+ }
+ },
+
+ new Test("Persistent #3") {
+ public void run() {
+ Notification n = new Notification.Builder(NotificationTestList.this, "low")
+ .setSmallIcon(R.drawable.icon1)
+ .setWhen(mActivityCreateTime)
+ .setContentTitle(name)
+ .setContentText("This is a notification!!!")
+ .setContentIntent(makeIntent())
+ .build();
+ mNM.notify(3, n);
+ }
+ },
+
+ new Test("Persistent #2 Vibrate") {
+ public void run() {
+ Notification n = new Notification.Builder(NotificationTestList.this, "low")
+ .setSmallIcon(R.drawable.icon1)
+ .setWhen(mActivityCreateTime)
+ .setContentTitle(name)
+ .setContentText("This is a notification!!!")
+ .setContentIntent(makeIntent())
+ .setDefaults(Notification.DEFAULT_VIBRATE)
+ .build();
+ mNM.notify(2, n);
+ }
+ },
+
+ new Test("Persistent #1 - different icon") {
+ public void run() {
+ Notification n = new Notification.Builder(NotificationTestList.this, "low")
+ .setSmallIcon(R.drawable.icon2)
+ .setWhen(mActivityCreateTime)
+ .setContentTitle(name)
+ .setContentText("This is a notification!!!")
+ .setContentIntent(makeIntent())
+ .build();
+ mNM.notify(1, n);
+ }
+ },
+
+ new Test("Chronometer Start") {
+ public void run() {
+ Notification n = new Notification.Builder(NotificationTestList.this, "low")
+ .setSmallIcon(R.drawable.icon1)
+ .setWhen(System.currentTimeMillis())
+ .setContentTitle(name)
+ .setContentIntent(makeIntent())
+ .setOngoing(true)
+ .setUsesChronometer(true)
+ .build();
+ mNM.notify(2, n);
+ }
+ },
+
+ new Test("Chronometer Stop") {
+ public void run() {
+ mHandler.postDelayed(new Runnable() {
public void run() {
Log.d(TAG, "Chronometer Stop");
Notification n = new Notification.Builder(
@@ -1024,121 +1024,121 @@
mNM.notify(2, n);
}
}, 3000);
- }
- },
-
- new Test("Sequential Persistent") {
- public void run() {
- mNM.notify(1, notificationWithNumbers(name, 1));
- mNM.notify(2, notificationWithNumbers(name, 2));
- }
- },
-
- new Test("Replace Persistent") {
- public void run() {
- mNM.notify(1, notificationWithNumbers(name, 1));
- mNM.notify(1, notificationWithNumbers(name, 1));
- }
- },
-
- new Test("Run and Cancel (n=1)") {
- public void run() {
- mNM.notify(1, notificationWithNumbers(name, 1));
- mNM.cancel(1);
- }
- },
-
- new Test("Run an Cancel (n=2)") {
- public void run() {
- mNM.notify(1, notificationWithNumbers(name, 1));
- mNM.notify(2, notificationWithNumbers(name, 2));
- mNM.cancel(2);
- }
- },
-
- // Repeatedly notify and cancel -- triggers bug #670627
- new Test("Bug 670627") {
- public void run() {
- for (int i = 0; i < 10; i++) {
- Log.d(TAG, "Add two notifications");
- mNM.notify(1, notificationWithNumbers(name, 1));
- mNM.notify(2, notificationWithNumbers(name, 2));
- Log.d(TAG, "Cancel two notifications");
- mNM.cancel(1);
- mNM.cancel(2);
}
- }
- },
+ },
- new Test("Ten Notifications") {
- public void run() {
- for (int i = 0; i < 10; i++) {
- Notification n = new Notification.Builder(NotificationTestList.this, "low")
- .setSmallIcon(kNumberedIconResIDs[i])
- .setContentTitle("Persistent #" + i)
- .setContentText("Notify me!!!" + i)
- .setOngoing(i < 2)
- .setNumber(i)
- .build();
- mNM.notify((i+1)*10, n);
+ new Test("Sequential Persistent") {
+ public void run() {
+ mNM.notify(1, notificationWithNumbers(name, 1));
+ mNM.notify(2, notificationWithNumbers(name, 2));
}
- }
- },
-
- new Test("Cancel eight notifications") {
- public void run() {
- for (int i = 1; i < 9; i++) {
- mNM.cancel((i+1)*10);
+ },
+
+ new Test("Replace Persistent") {
+ public void run() {
+ mNM.notify(1, notificationWithNumbers(name, 1));
+ mNM.notify(1, notificationWithNumbers(name, 1));
}
- }
- },
-
- new Test("Cancel the other two notifications") {
- public void run() {
- mNM.cancel(10);
- mNM.cancel(100);
- }
- },
-
- new Test("Persistent with numbers 1") {
- public void run() {
- mNM.notify(1, notificationWithNumbers(name, 1));
- }
- },
+ },
- new Test("Persistent with numbers 22") {
- public void run() {
- mNM.notify(1, notificationWithNumbers(name, 22));
- }
- },
+ new Test("Run and Cancel (n=1)") {
+ public void run() {
+ mNM.notify(1, notificationWithNumbers(name, 1));
+ mNM.cancel(1);
+ }
+ },
- new Test("Persistent with numbers 333") {
- public void run() {
- mNM.notify(1, notificationWithNumbers(name, 333));
- }
- },
+ new Test("Run an Cancel (n=2)") {
+ public void run() {
+ mNM.notify(1, notificationWithNumbers(name, 1));
+ mNM.notify(2, notificationWithNumbers(name, 2));
+ mNM.cancel(2);
+ }
+ },
- new Test("Persistent with numbers 4444") {
- public void run() {
- mNM.notify(1, notificationWithNumbers(name, 4444));
- }
- },
+ // Repeatedly notify and cancel -- triggers bug #670627
+ new Test("Bug 670627") {
+ public void run() {
+ for (int i = 0; i < 10; i++) {
+ Log.d(TAG, "Add two notifications");
+ mNM.notify(1, notificationWithNumbers(name, 1));
+ mNM.notify(2, notificationWithNumbers(name, 2));
+ Log.d(TAG, "Cancel two notifications");
+ mNM.cancel(1);
+ mNM.cancel(2);
+ }
+ }
+ },
- new Test("Crash") {
- public void run()
- {
- PowerManager.WakeLock wl =
- ((PowerManager) NotificationTestList.this.getSystemService(Context.POWER_SERVICE))
- .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "crasher");
- wl.acquire();
- mHandler.postDelayed(new Runnable() {
- public void run() {
- throw new RuntimeException("Die!");
- }
- }, 10000);
+ new Test("Ten Notifications") {
+ public void run() {
+ for (int i = 0; i < 10; i++) {
+ Notification n = new Notification.Builder(NotificationTestList.this, "low")
+ .setSmallIcon(kNumberedIconResIDs[i])
+ .setContentTitle("Persistent #" + i)
+ .setContentText("Notify me!!!" + i)
+ .setOngoing(i < 2)
+ .setNumber(i)
+ .build();
+ mNM.notify((i+1)*10, n);
+ }
+ }
+ },
- }
- },
+ new Test("Cancel eight notifications") {
+ public void run() {
+ for (int i = 1; i < 9; i++) {
+ mNM.cancel((i+1)*10);
+ }
+ }
+ },
+
+ new Test("Cancel the other two notifications") {
+ public void run() {
+ mNM.cancel(10);
+ mNM.cancel(100);
+ }
+ },
+
+ new Test("Persistent with numbers 1") {
+ public void run() {
+ mNM.notify(1, notificationWithNumbers(name, 1));
+ }
+ },
+
+ new Test("Persistent with numbers 22") {
+ public void run() {
+ mNM.notify(1, notificationWithNumbers(name, 22));
+ }
+ },
+
+ new Test("Persistent with numbers 333") {
+ public void run() {
+ mNM.notify(1, notificationWithNumbers(name, 333));
+ }
+ },
+
+ new Test("Persistent with numbers 4444") {
+ public void run() {
+ mNM.notify(1, notificationWithNumbers(name, 4444));
+ }
+ },
+
+ new Test("Crash") {
+ public void run()
+ {
+ PowerManager.WakeLock wl =
+ ((PowerManager) NotificationTestList.this.getSystemService(Context.POWER_SERVICE))
+ .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "crasher");
+ wl.acquire();
+ mHandler.postDelayed(new Runnable() {
+ public void run() {
+ throw new RuntimeException("Die!");
+ }
+ }, 10000);
+
+ }
+ },
};
@@ -1212,4 +1212,3 @@
return Bitmap.createBitmap(bd.getBitmap());
}
}
-
diff --git a/tools/aapt2/Main.cpp b/tools/aapt2/Main.cpp
index 5978cdd..c5d38ab 100644
--- a/tools/aapt2/Main.cpp
+++ b/tools/aapt2/Main.cpp
@@ -27,7 +27,7 @@
static const char* sMajorVersion = "2";
// Update minor version whenever a feature or flag is added.
-static const char* sMinorVersion = "17";
+static const char* sMinorVersion = "18";
int PrintVersion() {
std::cerr << "Android Asset Packaging Tool (aapt) " << sMajorVersion << "."