summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/INotificationManager.aidl2
-rw-r--r--core/java/android/provider/Settings.java16
-rw-r--r--core/java/android/security/IKeystoreService.java16
-rw-r--r--core/res/res/values/public.xml10
-rw-r--r--core/res/res/values/strings.xml11
-rw-r--r--core/res/res/values/symbols.xml2
-rw-r--r--keystore/java/android/security/KeyStore.java4
-rw-r--r--keystore/tests/src/android/security/KeyStoreTest.java36
-rw-r--r--libs/hwui/Caches.cpp1
-rw-r--r--libs/hwui/DisplayList.cpp6
-rw-r--r--libs/hwui/OpenGLRenderer.cpp12
-rw-r--r--libs/hwui/OpenGLRenderer.h2
-rw-r--r--libs/hwui/PathCache.cpp7
-rw-r--r--libs/hwui/PathCache.h16
-rw-r--r--libs/hwui/Snapshot.cpp11
-rw-r--r--libs/hwui/Snapshot.h2
-rw-r--r--libs/hwui/thread/TaskManager.cpp6
-rw-r--r--libs/hwui/thread/TaskManager.h6
-rw-r--r--libs/hwui/utils/Pair.h58
-rw-r--r--services/java/com/android/server/NotificationManagerService.java129
-rw-r--r--services/java/com/android/server/am/ServiceRecord.java41
-rw-r--r--services/java/com/android/server/wifi/WifiService.java12
-rw-r--r--wifi/java/android/net/wifi/WifiManager.java9
23 files changed, 349 insertions, 66 deletions
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 14bcc0d7fba4..3d9b2ae83518 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -41,7 +41,7 @@ interface INotificationManager
StatusBarNotification[] getActiveNotifications(String callingPkg);
StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count);
- void registerListener(in INotificationListener listener, int userid);
+ void registerListener(in INotificationListener listener, String pkg, int userid);
void unregisterListener(in INotificationListener listener, int userid);
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index d251ca25e068..19283be3897f 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -4039,6 +4039,14 @@ public final class Settings {
public static final String SCREENSAVER_DEFAULT_COMPONENT = "screensaver_default_component";
/**
+ * Name of a package that the current user has explicitly allowed to see all of that
+ * user's notifications.
+ *
+ * @hide
+ */
+ public static final String ENABLED_NOTIFICATION_LISTENERS = "enabled_notification_listeners";
+
+ /**
* This are the settings to be backed up.
*
* NOTE: Settings are backed up and restored in the order they appear
@@ -4791,6 +4799,14 @@ public final class Settings {
"wifi_scan_always_enabled";
/**
+ * Setting to indicate whether the user should be notified that scans are still
+ * available when Wi-Fi is turned off
+ * @hide
+ */
+ public static final String WIFI_NOTIFY_SCAN_ALWAYS_AVAILABLE =
+ "wifi_notify_scan_always_enabled";
+
+ /**
* Used to save the Wifi_ON state prior to tethering.
* This state will be checked to restore Wifi after
* the user turns off tethering.
diff --git a/core/java/android/security/IKeystoreService.java b/core/java/android/security/IKeystoreService.java
index 2ae3c6415d20..a890d9bdcd84 100644
--- a/core/java/android/security/IKeystoreService.java
+++ b/core/java/android/security/IKeystoreService.java
@@ -407,15 +407,18 @@ public interface IKeystoreService extends IInterface {
}
@Override
- public int migrate(String name, int targetUid) throws RemoteException {
+ public int duplicate(String srcKey, int srcUid, String destKey, int destUid)
+ throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
- _data.writeString(name);
- _data.writeInt(targetUid);
- mRemote.transact(Stub.TRANSACTION_migrate, _data, _reply, 0);
+ _data.writeString(srcKey);
+ _data.writeInt(srcUid);
+ _data.writeString(destKey);
+ _data.writeInt(destUid);
+ mRemote.transact(Stub.TRANSACTION_duplicate, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
@@ -448,7 +451,7 @@ public interface IKeystoreService extends IInterface {
static final int TRANSACTION_grant = IBinder.FIRST_CALL_TRANSACTION + 17;
static final int TRANSACTION_ungrant = IBinder.FIRST_CALL_TRANSACTION + 18;
static final int TRANSACTION_getmtime = IBinder.FIRST_CALL_TRANSACTION + 19;
- static final int TRANSACTION_migrate = IBinder.FIRST_CALL_TRANSACTION + 20;
+ static final int TRANSACTION_duplicate = IBinder.FIRST_CALL_TRANSACTION + 20;
/**
* Cast an IBinder object into an IKeystoreService interface, generating
@@ -534,5 +537,6 @@ public interface IKeystoreService extends IInterface {
public long getmtime(String name) throws RemoteException;
- public int migrate(String name, int targetUid) throws RemoteException;
+ public int duplicate(String srcKey, int srcUid, String destKey, int destUid)
+ throws RemoteException;
}
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index ef0ae03a371f..9f810af3ecc8 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2033,14 +2033,8 @@
=============================================================== -->
<eat-comment />
- <public type="attr" name="mipMap" id="0x010103cd" />
- <public type="attr" name="mirrorForRtl" id="0x010103ce" />
-
- <!-- ===============================================================
- Resources added in version 19 of the platform
- =============================================================== -->
- <eat-comment />
-
+ <public type="attr" name="mipMap" />
+ <public type="attr" name="mirrorForRtl" />
<public type="attr" name="windowOverscan" />
<public type="attr" name="requiredForAllUsers" />
<public type="style" name="Theme.NoTitleBar.Overscan" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index e497f1baa823..a2d45708e919 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2989,6 +2989,15 @@
<!-- If the device is getting low on internal storage, a notification is shown to the user. This is the message of that notification. -->
<string name="low_internal_storage_view_text">Some system functions may not work</string>
+ <!-- [CHAR LIMIT=NONE] Stub notification title for an app running a service that has provided
+ a bad bad notification for itself. -->
+ <string name="app_running_notification_title"><xliff:g id="app_name">%1$s</xliff:g>
+ running</string>
+ <!-- [CHAR LIMIT=NONE] Stub notification text for an app running a service that has provided
+ a bad bad notification for itself. -->
+ <string name="app_running_notification_text"><xliff:g id="app_name">%1$s</xliff:g>
+ is currently running</string>
+
<!-- Preference framework strings. -->
<string name="ok">OK</string>
<!-- Preference framework strings. -->
@@ -4040,7 +4049,7 @@
<!-- Message shown in dialog when user is attempting to set the music volume above the
recommended maximum level for headphones -->
<string name="safe_media_volume_warning" product="default">
- "Raise volume above safe level?\nListening at high volume for long periods may damage your hearing."
+ "Raise volume above recommended level?\nListening at high volume for long periods may damage your hearing."
</string>
<!-- Text spoken when the user is performing a gesture that will enable accessibility. [CHAR LIMIT=none] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 9ec33bb4bb46..80e77dd6e187 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -330,6 +330,8 @@
<java-symbol type="string" name="addToDictionary" />
<java-symbol type="string" name="action_bar_home_description" />
<java-symbol type="string" name="action_bar_up_description" />
+ <java-symbol type="string" name="app_running_notification_title" />
+ <java-symbol type="string" name="app_running_notification_text" />
<java-symbol type="string" name="delete" />
<java-symbol type="string" name="deleteText" />
<java-symbol type="string" name="ellipsis_two_dots" />
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 4dc0beb6cf9e..12c0ed8b217f 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -287,9 +287,9 @@ public class KeyStore {
}
}
- public boolean migrate(String key, int uid) {
+ public boolean duplicate(String srcKey, int srcUid, String destKey, int destUid) {
try {
- return mBinder.migrate(key, uid) == NO_ERROR;
+ return mBinder.duplicate(srcKey, srcUid, destKey, destUid) == NO_ERROR;
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
return false;
diff --git a/keystore/tests/src/android/security/KeyStoreTest.java b/keystore/tests/src/android/security/KeyStoreTest.java
index 8f8ee9215764..1de1eaf5bd50 100644
--- a/keystore/tests/src/android/security/KeyStoreTest.java
+++ b/keystore/tests/src/android/security/KeyStoreTest.java
@@ -553,7 +553,7 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> {
mKeyStore.ungrant(TEST_KEYNAME, 0));
}
- public void testMigrate_grantedUid_Wifi_Success() throws Exception {
+ public void testDuplicate_grantedUid_Wifi_Success() throws Exception {
assertTrue(mKeyStore.password(TEST_PASSWD));
assertFalse(mKeyStore.contains(TEST_KEYNAME));
@@ -563,13 +563,35 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> {
assertTrue(mKeyStore.contains(TEST_KEYNAME));
assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
- assertTrue(mKeyStore.migrate(TEST_KEYNAME, Process.WIFI_UID));
+ // source doesn't exist
+ assertFalse(mKeyStore.duplicate(TEST_KEYNAME1, -1, TEST_KEYNAME1, Process.WIFI_UID));
+ assertFalse(mKeyStore.contains(TEST_KEYNAME1, Process.WIFI_UID));
- assertFalse(mKeyStore.contains(TEST_KEYNAME));
- assertTrue(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
+ // Copy from current UID to granted UID
+ assertTrue(mKeyStore.duplicate(TEST_KEYNAME, -1, TEST_KEYNAME1, Process.WIFI_UID));
+ assertTrue(mKeyStore.contains(TEST_KEYNAME));
+ assertFalse(mKeyStore.contains(TEST_KEYNAME1));
+ assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
+ assertTrue(mKeyStore.contains(TEST_KEYNAME1, Process.WIFI_UID));
+ assertFalse(mKeyStore.duplicate(TEST_KEYNAME, -1, TEST_KEYNAME1, Process.WIFI_UID));
+
+ // Copy from granted UID to same granted UID
+ assertTrue(mKeyStore.duplicate(TEST_KEYNAME1, Process.WIFI_UID, TEST_KEYNAME2,
+ Process.WIFI_UID));
+ assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
+ assertTrue(mKeyStore.contains(TEST_KEYNAME1, Process.WIFI_UID));
+ assertTrue(mKeyStore.contains(TEST_KEYNAME2, Process.WIFI_UID));
+ assertFalse(mKeyStore.duplicate(TEST_KEYNAME1, Process.WIFI_UID, TEST_KEYNAME2,
+ Process.WIFI_UID));
+
+ assertTrue(mKeyStore.duplicate(TEST_KEYNAME, -1, TEST_KEYNAME2, -1));
+ assertTrue(mKeyStore.contains(TEST_KEYNAME));
+ assertFalse(mKeyStore.contains(TEST_KEYNAME1));
+ assertTrue(mKeyStore.contains(TEST_KEYNAME2));
+ assertFalse(mKeyStore.duplicate(TEST_KEYNAME, -1, TEST_KEYNAME2, -1));
}
- public void testMigrate_ungrantedUid_Bluetooth_Failure() throws Exception {
+ public void testDuplicate_ungrantedUid_Bluetooth_Failure() throws Exception {
assertTrue(mKeyStore.password(TEST_PASSWD));
assertFalse(mKeyStore.contains(TEST_KEYNAME));
@@ -579,7 +601,9 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> {
assertTrue(mKeyStore.contains(TEST_KEYNAME));
assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.BLUETOOTH_UID));
- assertFalse(mKeyStore.migrate(TEST_KEYNAME, Process.BLUETOOTH_UID));
+ assertFalse(mKeyStore.duplicate(TEST_KEYNAME, -1, TEST_KEYNAME2, Process.BLUETOOTH_UID));
+ assertFalse(mKeyStore.duplicate(TEST_KEYNAME, Process.BLUETOOTH_UID, TEST_KEYNAME2,
+ Process.BLUETOOTH_UID));
assertTrue(mKeyStore.contains(TEST_KEYNAME));
assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.BLUETOOTH_UID));
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index dc3a4e2dc8cb..57d1a4f27a55 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -310,6 +310,7 @@ void Caches::flush(FlushMode mode) {
fontRenderer->flush();
textureCache.flush();
pathCache.clear();
+ tasks.stop();
// fall through
case kFlushMode_Layers:
layerCache.clear();
diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp
index 55ddd17f5cb6..d985ad00e849 100644
--- a/libs/hwui/DisplayList.cpp
+++ b/libs/hwui/DisplayList.cpp
@@ -351,9 +351,9 @@ void DisplayList::outputViewProperties(const int level) {
level * 2, "", mTransformMatrix, MATRIX_ARGS(mTransformMatrix));
}
}
- if (mAlpha < 1 && !mCaching) {
- if (!mHasOverlappingRendering) {
- ALOGD("%*sSetAlpha %.2f", level * 2, "", mAlpha);
+ if (mAlpha < 1) {
+ if (mCaching || !mHasOverlappingRendering) {
+ ALOGD("%*sScaleAlpha %.2f", level * 2, "", mAlpha);
} else {
int flags = SkCanvas::kHasAlphaLayer_SaveFlag;
if (mClipChildren) {
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index bc28d655aec4..2cf7183360d1 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -193,6 +193,7 @@ status_t OpenGLRenderer::prepareDirty(float left, float top,
mSaveCount = 1;
mSnapshot->setClip(left, top, right, bottom);
+ mTilingClip.set(left, top, right, bottom);
mDirtyClip = true;
updateLayers();
@@ -206,8 +207,7 @@ status_t OpenGLRenderer::prepareDirty(float left, float top,
// invoked during the frame
mSuppressTiling = mCaches.hasRegisteredFunctors();
- mTilingSnapshot = mSnapshot;
- startTiling(mTilingSnapshot, true);
+ startTiling(mSnapshot, true);
debugOverdraw(true, true);
@@ -252,9 +252,9 @@ void OpenGLRenderer::syncState() {
void OpenGLRenderer::startTiling(const sp<Snapshot>& s, bool opaque) {
if (!mSuppressTiling) {
- Rect* clip = mTilingSnapshot->clipRect;
+ Rect* clip = &mTilingClip;
if (s->flags & Snapshot::kFlagFboTarget) {
- clip = &s->layer->clipRect;
+ clip = &(s->layer->clipRect);
}
startTiling(*clip, s->height, opaque);
@@ -480,10 +480,10 @@ void OpenGLRenderer::debugOverdraw(bool enable, bool clear) {
void OpenGLRenderer::renderOverdraw() {
if (mCaches.debugOverdraw && getTargetFbo() == 0) {
- const Rect* clip = mTilingSnapshot->clipRect;
+ const Rect* clip = &mTilingClip;
mCaches.enableScissor();
- mCaches.setScissor(clip->left, mTilingSnapshot->height - clip->bottom,
+ mCaches.setScissor(clip->left, mFirstSnapshot->height - clip->bottom,
clip->right - clip->left, clip->bottom - clip->top);
mCaches.stencil.enableDebugTest(2);
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 71bd6bb124f0..7bb93957bc03 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -947,7 +947,7 @@ private:
// Current state
sp<Snapshot> mSnapshot;
// State used to define the clipping region
- sp<Snapshot> mTilingSnapshot;
+ Rect mTilingClip;
// Used to draw textured quads
TextureVertex mMeshVertices[4];
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index 490c22a00553..07c420721fa5 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -347,14 +347,15 @@ void PathCache::PathProcessor::onProcess(const sp<Task<SkBitmap*> >& task) {
// Paths
///////////////////////////////////////////////////////////////////////////////
-void PathCache::remove(SkPath* path) {
+void PathCache::remove(const path_pair_t& pair) {
Vector<PathDescription> pathsToRemove;
LruCache<PathDescription, PathTexture*>::Iterator i(mCache);
while (i.next()) {
const PathDescription& key = i.key();
if (key.type == kShapePath &&
- (key.shape.path.mPath == path || key.shape.path.mPath == path->getSourcePath())) {
+ (key.shape.path.mPath == pair.getFirst() ||
+ key.shape.path.mPath == pair.getSecond())) {
pathsToRemove.push(key);
}
}
@@ -366,7 +367,7 @@ void PathCache::remove(SkPath* path) {
void PathCache::removeDeferred(SkPath* path) {
Mutex::Autolock l(mLock);
- mGarbage.push(path);
+ mGarbage.push(path_pair_t(path, const_cast<SkPath*>(path->getSourcePath())));
}
void PathCache::clearGarbage() {
diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h
index e6d92df0b06c..146723162fa5 100644
--- a/libs/hwui/PathCache.h
+++ b/libs/hwui/PathCache.h
@@ -26,6 +26,7 @@
#include "Debug.h"
#include "Properties.h"
#include "Texture.h"
+#include "utils/Pair.h"
class SkBitmap;
class SkCanvas;
@@ -215,10 +216,6 @@ public:
PathTexture* get(SkPath* path, SkPaint* paint);
/**
- * Removes an entry.
- */
- void remove(SkPath* path);
- /**
* Removes the specified path. This is meant to be called from threads
* that are not the EGL context thread.
*/
@@ -251,6 +248,8 @@ public:
float& left, float& top, float& offset, uint32_t& width, uint32_t& height);
private:
+ typedef Pair<SkPath*, SkPath*> path_pair_t;
+
PathTexture* addTexture(const PathDescription& entry,
const SkPath *path, const SkPaint* paint);
PathTexture* addTexture(const PathDescription& entry, SkBitmap* bitmap);
@@ -261,6 +260,12 @@ private:
}
/**
+ * Removes an entry.
+ * The pair must define first=path, second=sourcePath
+ */
+ void remove(const path_pair_t& pair);
+
+ /**
* Ensures there is enough space in the cache for a texture of the specified
* dimensions.
*/
@@ -318,7 +323,8 @@ private:
bool mDebugEnabled;
sp<PathProcessor> mProcessor;
- Vector<SkPath*> mGarbage;
+
+ Vector<path_pair_t> mGarbage;
mutable Mutex mLock;
}; // class PathCache
diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp
index 923913e864a9..d26ee3884433 100644
--- a/libs/hwui/Snapshot.cpp
+++ b/libs/hwui/Snapshot.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#define LOG_TAG "OpenGLRenderer"
+
#include "Snapshot.h"
#include <SkCanvas.h>
@@ -199,5 +201,14 @@ bool Snapshot::isIgnored() const {
return invisible || empty;
}
+void Snapshot::dump() const {
+ ALOGD("Snapshot %p, flags %x, prev %p, height %d, ignored %d, hasComplexClip %d",
+ this, flags, previous.get(), height, isIgnored(), clipRegion && !clipRegion->isEmpty());
+ ALOGD(" ClipRect (at %p) %.1f %.1f %.1f %.1f",
+ clipRect, clipRect->left, clipRect->top, clipRect->right, clipRect->bottom);
+ ALOGD(" Transform (at %p):", transform);
+ transform->dump();
+}
+
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h
index ffd47295b618..cc6d0cda003e 100644
--- a/libs/hwui/Snapshot.h
+++ b/libs/hwui/Snapshot.h
@@ -228,6 +228,8 @@ public:
*/
float alpha;
+ void dump() const;
+
private:
void ensureClipRegion();
void copyClipRectFromRegion();
diff --git a/libs/hwui/thread/TaskManager.cpp b/libs/hwui/thread/TaskManager.cpp
index ce6c8c04483d..c8bfd9c88165 100644
--- a/libs/hwui/thread/TaskManager.cpp
+++ b/libs/hwui/thread/TaskManager.cpp
@@ -48,6 +48,12 @@ bool TaskManager::canRunTasks() const {
return mThreads.size() > 0;
}
+void TaskManager::stop() {
+ for (size_t i = 0; i < mThreads.size(); i++) {
+ mThreads[i]->exit();
+ }
+}
+
bool TaskManager::addTaskBase(const sp<TaskBase>& task, const sp<TaskProcessorBase>& processor) {
if (mThreads.size() > 0) {
TaskWrapper wrapper(task, processor);
diff --git a/libs/hwui/thread/TaskManager.h b/libs/hwui/thread/TaskManager.h
index bc86062819cc..477314bbbb00 100644
--- a/libs/hwui/thread/TaskManager.h
+++ b/libs/hwui/thread/TaskManager.h
@@ -47,6 +47,12 @@ public:
*/
bool canRunTasks() const;
+ /**
+ * Stops all allocated threads. Adding tasks will start
+ * the threads again as necessary.
+ */
+ void stop();
+
private:
template <typename T>
friend class TaskProcessor;
diff --git a/libs/hwui/utils/Pair.h b/libs/hwui/utils/Pair.h
new file mode 100644
index 000000000000..172606a4d586
--- /dev/null
+++ b/libs/hwui/utils/Pair.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#ifndef ANDROID_HWUI_PAIR_H
+#define ANDROID_HWUI_PAIR_H
+
+namespace android {
+namespace uirenderer {
+
+template <typename F, typename S>
+struct Pair {
+ F first;
+ S second;
+
+ Pair() { }
+ Pair(const Pair& o) : first(o.first), second(o.second) { }
+ Pair(const F& f, const S& s) : first(f), second(s) { }
+
+ inline const F& getFirst() const {
+ return first;
+ }
+
+ inline const S& getSecond() const {
+ return second;
+ }
+};
+
+}; // namespace uirenderer
+
+template <typename F, typename S>
+struct trait_trivial_ctor< uirenderer::Pair<F, S> >
+{ enum { value = aggregate_traits<F, S>::has_trivial_ctor }; };
+template <typename F, typename S>
+struct trait_trivial_dtor< uirenderer::Pair<F, S> >
+{ enum { value = aggregate_traits<F, S>::has_trivial_dtor }; };
+template <typename F, typename S>
+struct trait_trivial_copy< uirenderer::Pair<F, S> >
+{ enum { value = aggregate_traits<F, S>::has_trivial_copy }; };
+template <typename F, typename S>
+struct trait_trivial_move< uirenderer::Pair<F, S> >
+{ enum { value = aggregate_traits<F, S>::has_trivial_move }; };
+
+}; // namespace android
+
+#endif // ANDROID_HWUI_PAIR_H
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 9e036d1124ec..5cf1c285f2db 100644
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -50,12 +50,11 @@ import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
-import android.os.Parcel;
-import android.os.Parcelable;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
+import android.os.UserManager;
import android.os.Vibrator;
import android.provider.Settings;
import android.telephony.TelephonyManager;
@@ -124,6 +123,7 @@ public class NotificationManagerService extends INotificationManager.Stub
final Context mContext;
final IActivityManager mAm;
+ final UserManager mUserManager;
final IBinder mForegroundToken = new Binder();
private WorkerHandler mHandler;
@@ -164,6 +164,7 @@ public class NotificationManagerService extends INotificationManager.Stub
private final AppOpsManager mAppOps;
private ArrayList<NotificationListenerInfo> mListeners = new ArrayList<NotificationListenerInfo>();
+ private ArrayList<String> mEnabledListenersForCurrentUser = new ArrayList<String>();
// Notification control database. For now just contains disabled packages.
private AtomicFile mPolicyFile;
@@ -180,20 +181,27 @@ public class NotificationManagerService extends INotificationManager.Stub
private class NotificationListenerInfo implements DeathRecipient {
INotificationListener listener;
+ String pkg;
int userid;
- public NotificationListenerInfo(INotificationListener listener, int userid) {
+ boolean isSystem;
+
+ public NotificationListenerInfo(INotificationListener listener, String pkg, int userid,
+ boolean isSystem) {
this.listener = listener;
+ this.pkg = pkg;
this.userid = userid;
+ this.isSystem = isSystem;
}
- boolean userMatches(StatusBarNotification sbn) {
+ boolean enabledAndUserMatches(StatusBarNotification sbn) {
+ final int nid = sbn.getUserId();
+ if (!(isSystem || isEnabledForUser(nid))) return false;
if (this.userid == UserHandle.USER_ALL) return true;
- int nid = sbn.getUserId();
return (nid == UserHandle.USER_ALL || nid == this.userid);
}
public void notifyPostedIfUserMatch(StatusBarNotification sbn) {
- if (!userMatches(sbn)) return;
+ if (!enabledAndUserMatches(sbn)) return;
try {
listener.onNotificationPosted(sbn);
} catch (RemoteException ex) {
@@ -202,7 +210,7 @@ public class NotificationManagerService extends INotificationManager.Stub
}
public void notifyRemovedIfUserMatch(StatusBarNotification sbn) {
- if (!userMatches(sbn)) return;
+ if (!enabledAndUserMatches(sbn)) return;
try {
listener.onNotificationRemoved(sbn);
} catch (RemoteException ex) {
@@ -214,6 +222,14 @@ public class NotificationManagerService extends INotificationManager.Stub
public void binderDied() {
unregisterListener(this.listener, this.userid);
}
+
+ /** convenience method for looking in mEnabledListenersForCurrentUser */
+ public boolean isEnabledForUser(int userid) {
+ for (int i=0; i<mEnabledListenersForCurrentUser.size(); i++) {
+ if (this.pkg.equals(mEnabledListenersForCurrentUser.get(i))) return true;
+ }
+ return false;
+ }
}
private static class Archive {
@@ -413,12 +429,14 @@ public class NotificationManagerService extends INotificationManager.Stub
}
public StatusBarNotification[] getActiveNotifications(String callingPkg) {
+ // enforce() will ensure the calling uid has the correct permission
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_NOTIFICATIONS,
"NotificationManagerService.getActiveNotifications");
StatusBarNotification[] tmp = null;
int uid = Binder.getCallingUid();
+ // noteOp will check to make sure the callingPkg matches the uid
if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
== AppOpsManager.MODE_ALLOWED) {
synchronized (mNotificationList) {
@@ -433,12 +451,14 @@ public class NotificationManagerService extends INotificationManager.Stub
}
public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) {
+ // enforce() will ensure the calling uid has the correct permission
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_NOTIFICATIONS,
"NotificationManagerService.getHistoricalNotifications");
StatusBarNotification[] tmp = null;
int uid = Binder.getCallingUid();
+ // noteOp will check to make sure the callingPkg matches the uid
if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
== AppOpsManager.MODE_ALLOWED) {
synchronized (mArchive) {
@@ -448,12 +468,27 @@ public class NotificationManagerService extends INotificationManager.Stub
return tmp;
}
+ boolean packageCanTapNotificationsForUser(final int uid, final String pkg) {
+ // Make sure the package and uid match, and that the package is allowed access
+ return (AppOpsManager.MODE_ALLOWED
+ == mAppOps.checkOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, pkg));
+ }
+
@Override
- public void registerListener(final INotificationListener listener, final int userid) {
- checkCallerIsSystem();
+ public void registerListener(final INotificationListener listener,
+ final String pkg, final int userid) {
+ // ensure system or allowed pkg
+ int uid = Binder.getCallingUid();
+ boolean isSystem = (UserHandle.getAppId(uid) == Process.SYSTEM_UID || uid == 0);
+ if (!(isSystem || packageCanTapNotificationsForUser(uid, pkg))) {
+ throw new SecurityException("Package " + pkg
+ + " may not listen for notifications");
+ }
+
synchronized (mNotificationList) {
try {
- NotificationListenerInfo info = new NotificationListenerInfo(listener, userid);
+ NotificationListenerInfo info
+ = new NotificationListenerInfo(listener, pkg, userid, isSystem);
listener.asBinder().linkToDeath(info, 0);
mListeners.add(info);
} catch (RemoteException e) {
@@ -464,7 +499,9 @@ public class NotificationManagerService extends INotificationManager.Stub
@Override
public void unregisterListener(INotificationListener listener, int userid) {
- checkCallerIsSystem();
+ // no need to check permissions; if your listener binder is in the list,
+ // that's proof that you had permission to add it in the first place
+
synchronized (mNotificationList) {
final int N = mListeners.size();
for (int i=N-1; i>=0; i--) {
@@ -740,37 +777,67 @@ public class NotificationManagerService extends INotificationManager.Stub
} else if (action.equals(Intent.ACTION_USER_PRESENT)) {
// turn off LED when user passes through lock screen
mNotificationLight.turnOff();
+ } else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
+ // reload per-user settings
+ mSettingsObserver.update(null);
}
}
};
class SettingsObserver extends ContentObserver {
+ private final Uri NOTIFICATION_LIGHT_PULSE_URI
+ = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE);
+
+ private final Uri ENABLED_NOTIFICATION_LISTENERS_URI
+ = Settings.System.getUriFor(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
+
SettingsObserver(Handler handler) {
super(handler);
}
void observe() {
ContentResolver resolver = mContext.getContentResolver();
- resolver.registerContentObserver(Settings.System.getUriFor(
- Settings.System.NOTIFICATION_LIGHT_PULSE), false, this);
- update();
+ resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI,
+ false, this);
+ resolver.registerContentObserver(ENABLED_NOTIFICATION_LISTENERS_URI,
+ false, this);
+ update(null);
}
- @Override public void onChange(boolean selfChange) {
- update();
+ @Override public void onChange(boolean selfChange, Uri uri) {
+ update(uri);
}
- public void update() {
+ public void update(Uri uri) {
ContentResolver resolver = mContext.getContentResolver();
- boolean pulseEnabled = Settings.System.getInt(resolver,
- Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0;
- if (mNotificationPulseEnabled != pulseEnabled) {
- mNotificationPulseEnabled = pulseEnabled;
- updateNotificationPulse();
+ if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) {
+ boolean pulseEnabled = Settings.System.getInt(resolver,
+ Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0;
+ if (mNotificationPulseEnabled != pulseEnabled) {
+ mNotificationPulseEnabled = pulseEnabled;
+ updateNotificationPulse();
+ }
+ }
+ if (uri == null || ENABLED_NOTIFICATION_LISTENERS_URI.equals(uri)) {
+ String pkglist = Settings.Secure.getString(
+ mContext.getContentResolver(),
+ Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
+ mEnabledListenersForCurrentUser.clear();
+ if (pkglist != null) {
+ String[] pkgs = pkglist.split(";");
+ for (int i=0; i<pkgs.length; i++) {
+ final String pkg = pkgs[i];
+ if (pkg != null && ! "".equals(pkg)) {
+ mEnabledListenersForCurrentUser.add(pkgs[i]);
+ }
+ }
+ }
}
}
}
+ private SettingsObserver mSettingsObserver;
+
static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) {
int[] ar = r.getIntArray(resid);
if (ar == null) {
@@ -791,6 +858,7 @@ public class NotificationManagerService extends INotificationManager.Stub
mContext = context;
mVibrator = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE);
mAm = ActivityManagerNative.getDefault();
+ mUserManager = (UserManager)context.getSystemService(Context.USER_SERVICE);
mToastQueue = new ArrayList<ToastRecord>();
mHandler = new WorkerHandler();
@@ -838,6 +906,7 @@ public class NotificationManagerService extends INotificationManager.Stub
filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
filter.addAction(Intent.ACTION_USER_PRESENT);
filter.addAction(Intent.ACTION_USER_STOPPED);
+ filter.addAction(Intent.ACTION_USER_SWITCHED);
mContext.registerReceiver(mIntentReceiver, filter);
IntentFilter pkgFilter = new IntentFilter();
pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
@@ -849,8 +918,8 @@ public class NotificationManagerService extends INotificationManager.Stub
IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
mContext.registerReceiver(mIntentReceiver, sdFilter);
- SettingsObserver observer = new SettingsObserver(mHandler);
- observer.observe();
+ mSettingsObserver = new SettingsObserver(mHandler);
+ mSettingsObserver.observe();
}
/**
@@ -1706,6 +1775,18 @@ public class NotificationManagerService extends INotificationManager.Stub
pw.println("Current Notification Manager state:");
+ pw.print(" Enabled listeners: [");
+ for (String pkg : mEnabledListenersForCurrentUser) {
+ pw.print(" " + pkg);
+ }
+ pw.println(" ]");
+
+ pw.println(" Live listeners:");
+ for (NotificationListenerInfo info : mListeners) {
+ pw.println(" " + info.pkg + " (user " + info.userid + "): " + info.listener
+ + (info.isSystem?" SYSTEM":""));
+ }
+
int N;
synchronized (mToastQueue) {
diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java
index 1ac6bdfb850f..8ff1c7d5fba7 100644
--- a/services/java/com/android/server/am/ServiceRecord.java
+++ b/services/java/com/android/server/am/ServiceRecord.java
@@ -16,6 +16,9 @@
package com.android.server.am;
+import android.app.PendingIntent;
+import android.net.Uri;
+import android.provider.Settings;
import com.android.internal.os.BatteryStatsImpl;
import com.android.server.NotificationManagerService;
@@ -369,6 +372,44 @@ class ServiceRecord extends Binder {
}
try {
if (foregroundNoti.icon == 0) {
+ // It is not correct for the caller to supply a notification
+ // icon, but this used to be able to slip through, so for
+ // those dirty apps give it the app's icon.
+ foregroundNoti.icon = appInfo.icon;
+ if (foregroundNoti.contentView == null) {
+ // In this case the app may not have specified a
+ // content view... so we'll give them something to show.
+ CharSequence appName = appInfo.loadLabel(
+ ams.mContext.getPackageManager());
+ if (appName == null) {
+ appName = appInfo.packageName;
+ }
+ Context ctx = null;
+ try {
+ ctx = ams.mContext.createPackageContext(
+ appInfo.packageName, 0);
+ Intent runningIntent = new Intent(
+ Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
+ runningIntent.setData(Uri.fromParts("package",
+ appInfo.packageName, null));
+ PendingIntent pi = PendingIntent.getActivity(ams.mContext, 0,
+ runningIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+ foregroundNoti.setLatestEventInfo(ctx,
+ ams.mContext.getString(
+ com.android.internal.R.string
+ .app_running_notification_title,
+ appName),
+ ams.mContext.getString(
+ com.android.internal.R.string
+ .app_running_notification_text,
+ appName),
+ pi);
+ } catch (PackageManager.NameNotFoundException e) {
+ foregroundNoti.icon = 0;
+ }
+ }
+ }
+ if (foregroundNoti.icon == 0) {
// Notifications whose icon is 0 are defined to not show
// a notification, silently ignoring it. We don't want to
// just ignore it, we want to prevent the service from
diff --git a/services/java/com/android/server/wifi/WifiService.java b/services/java/com/android/server/wifi/WifiService.java
index c46f2174593e..9dde58f75975 100644
--- a/services/java/com/android/server/wifi/WifiService.java
+++ b/services/java/com/android/server/wifi/WifiService.java
@@ -396,6 +396,18 @@ public final class WifiService extends IWifiManager.Stub {
long ident = Binder.clearCallingIdentity();
try {
+
+ /* Turning off Wi-Fi when scans are still available */
+ if (!enable && isScanningAlwaysAvailable()) {
+ /* This can be changed by user in the app to not be notified again */
+ boolean notifyUser = (Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.WIFI_NOTIFY_SCAN_ALWAYS_AVAILABLE, 1) == 1);
+ if (notifyUser) {
+ Intent intent = new Intent(WifiManager.ACTION_NOTIFY_SCAN_ALWAYS_AVAILABLE);
+ mContext.startActivityAsUser(intent, null, UserHandle.CURRENT);
+ }
+ }
+
if (! mSettingsStore.handleWifiToggled(enable)) {
// Nothing to do if wifi cannot be toggled
return true;
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index f6543104ddd7..0c0a144ae234 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -408,6 +408,15 @@ public class WifiManager {
"android.net.wifi.action.REQUEST_SCAN_ALWAYS_AVAILABLE";
/**
+ * Activity Action: Show a system activity that notifies the user that
+ * scanning is still available when Wi-Fi is turned off
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_NOTIFY_SCAN_ALWAYS_AVAILABLE =
+ "android.net.wifi.action.NOTIFY_SCAN_ALWAYS_AVAILABLE";
+
+ /**
* Activity Action: Pick a Wi-Fi network to connect to.
* <p>Input: Nothing.
* <p>Output: Nothing.