Merge "Multi-user ringtone playback." into jb-mr1-dev
This commit is contained in:
@@ -124,6 +124,9 @@ public class NotificationManager
|
||||
int[] idOut = new int[1];
|
||||
INotificationManager service = getService();
|
||||
String pkg = mContext.getPackageName();
|
||||
if (notification.sound != null) {
|
||||
notification.sound = notification.sound.getCanonicalUri();
|
||||
}
|
||||
if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");
|
||||
try {
|
||||
service.enqueueNotificationWithTag(pkg, tag, id, notification, idOut,
|
||||
@@ -143,6 +146,9 @@ public class NotificationManager
|
||||
int[] idOut = new int[1];
|
||||
INotificationManager service = getService();
|
||||
String pkg = mContext.getPackageName();
|
||||
if (notification.sound != null) {
|
||||
notification.sound = notification.sound.getCanonicalUri();
|
||||
}
|
||||
if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");
|
||||
try {
|
||||
service.enqueueNotificationWithTag(pkg, tag, id, notification, idOut,
|
||||
|
||||
@@ -16,10 +16,13 @@
|
||||
|
||||
package android.net;
|
||||
|
||||
import android.os.Environment;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.os.Environment.UserEnvironment;
|
||||
import android.util.Log;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.Charsets;
|
||||
@@ -2288,4 +2291,39 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
|
||||
builder = builder.appendEncodedPath(pathSegment);
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* If this {@link Uri} is {@code file://}, then resolve and return its
|
||||
* canonical path. Also fixes legacy emulated storage paths so they are
|
||||
* usable across user boundaries. Should always be called from the app
|
||||
* process before sending elsewhere.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public Uri getCanonicalUri() {
|
||||
if ("file".equals(getScheme())) {
|
||||
final String canonicalPath;
|
||||
try {
|
||||
canonicalPath = new File(getPath()).getCanonicalPath();
|
||||
} catch (IOException e) {
|
||||
return this;
|
||||
}
|
||||
|
||||
if (Environment.isExternalStorageEmulated()) {
|
||||
final String legacyPath = Environment.getLegacyExternalStorageDirectory()
|
||||
.toString();
|
||||
|
||||
// Splice in user-specific path when legacy path is found
|
||||
if (canonicalPath.startsWith(legacyPath)) {
|
||||
return Uri.fromFile(new File(
|
||||
Environment.getExternalStorageDirectory().toString(),
|
||||
canonicalPath.substring(legacyPath.length() + 1)));
|
||||
}
|
||||
}
|
||||
|
||||
return Uri.fromFile(new File(canonicalPath));
|
||||
} else {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
19
core/java/android/os/UserHandle.aidl
Normal file
19
core/java/android/os/UserHandle.aidl
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* Copyright (c) 2012, The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.os;
|
||||
|
||||
parcelable UserHandle;
|
||||
@@ -2166,6 +2166,8 @@ public class RemoteViews implements Parcelable, Filter {
|
||||
* @param value The value to pass to the method.
|
||||
*/
|
||||
public void setUri(int viewId, String methodName, Uri value) {
|
||||
// Resolve any filesystem path before sending remotely
|
||||
value = value.getCanonicalUri();
|
||||
addAction(new ReflectionAction(viewId, methodName, ReflectionAction.URI, value));
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package android.media;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.os.UserHandle;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
@@ -28,6 +29,6 @@ interface IRingtonePlayer {
|
||||
boolean isPlaying(IBinder token);
|
||||
|
||||
/** Used for Notification sound playback. */
|
||||
void playAsync(in Uri uri, boolean looping, int streamType);
|
||||
void playAsync(in Uri uri, in UserHandle user, boolean looping, int streamType);
|
||||
void stopAsync();
|
||||
}
|
||||
|
||||
@@ -225,8 +225,9 @@ public class Ringtone {
|
||||
mLocalPlayer.start();
|
||||
}
|
||||
} else if (mAllowRemote) {
|
||||
final Uri canonicalUri = mUri.getCanonicalUri();
|
||||
try {
|
||||
mRemotePlayer.play(mRemoteToken, mUri, mStreamType);
|
||||
mRemotePlayer.play(mRemoteToken, canonicalUri, mStreamType);
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "Problem playing ringtone: " + e);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_ALL_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
|
||||
<uses-permission android:name="android.permission.INJECT_EVENTS" />
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.systemui.media;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.media.IAudioService;
|
||||
import android.media.IRingtonePlayer;
|
||||
import android.media.Ringtone;
|
||||
@@ -26,6 +27,7 @@ import android.os.IBinder;
|
||||
import android.os.Process;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.os.UserHandle;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.systemui.SystemUI;
|
||||
@@ -70,9 +72,10 @@ public class RingtonePlayer extends SystemUI {
|
||||
private final IBinder mToken;
|
||||
private final Ringtone mRingtone;
|
||||
|
||||
public Client(IBinder token, Uri uri, int streamType) {
|
||||
public Client(IBinder token, Uri uri, UserHandle user, int streamType) {
|
||||
mToken = token;
|
||||
mRingtone = new Ringtone(mContext, false);
|
||||
|
||||
mRingtone = new Ringtone(getContextForUser(user), false);
|
||||
mRingtone.setStreamType(streamType);
|
||||
mRingtone.setUri(uri);
|
||||
}
|
||||
@@ -90,12 +93,16 @@ public class RingtonePlayer extends SystemUI {
|
||||
private IRingtonePlayer mCallback = new IRingtonePlayer.Stub() {
|
||||
@Override
|
||||
public void play(IBinder token, Uri uri, int streamType) throws RemoteException {
|
||||
if (LOGD) Slog.d(TAG, "play(token=" + token + ", uri=" + uri + ")");
|
||||
if (LOGD) {
|
||||
Slog.d(TAG, "play(token=" + token + ", uri=" + uri + ", uid="
|
||||
+ Binder.getCallingUid() + ")");
|
||||
}
|
||||
Client client;
|
||||
synchronized (mClients) {
|
||||
client = mClients.get(token);
|
||||
if (client == null) {
|
||||
client = new Client(token, uri, streamType);
|
||||
final UserHandle user = Binder.getCallingUserHandle();
|
||||
client = new Client(token, uri, user, streamType);
|
||||
token.linkToDeath(client, 0);
|
||||
mClients.put(token, client);
|
||||
}
|
||||
@@ -131,12 +138,13 @@ public class RingtonePlayer extends SystemUI {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playAsync(Uri uri, boolean looping, int streamType) {
|
||||
if (LOGD) Slog.d(TAG, "playAsync(uri=" + uri + ")");
|
||||
public void playAsync(Uri uri, UserHandle user, boolean looping, int streamType) {
|
||||
if (LOGD) Slog.d(TAG, "playAsync(uri=" + uri + ", user=" + user + ")");
|
||||
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
|
||||
throw new SecurityException("Async playback only available from system UID.");
|
||||
}
|
||||
mAsyncPlayer.play(mContext, uri, looping, streamType);
|
||||
|
||||
mAsyncPlayer.play(getContextForUser(user), uri, looping, streamType);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -149,6 +157,14 @@ public class RingtonePlayer extends SystemUI {
|
||||
}
|
||||
};
|
||||
|
||||
private Context getContextForUser(UserHandle user) {
|
||||
try {
|
||||
return mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user);
|
||||
} catch (NameNotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
||||
pw.println("Clients:");
|
||||
|
||||
@@ -890,6 +890,7 @@ public class NotificationManagerService extends INotificationManager.Stub
|
||||
|
||||
userId = ActivityManager.handleIncomingUser(callingPid,
|
||||
callingUid, userId, true, true, "enqueueNotification", pkg);
|
||||
final UserHandle user = new UserHandle(userId);
|
||||
|
||||
// Limit the number of notifications that any given package except the android
|
||||
// package can enqueue. Prevents DOS attacks and deals with leaks.
|
||||
@@ -991,7 +992,6 @@ public class NotificationManagerService extends INotificationManager.Stub
|
||||
}
|
||||
|
||||
if (notification.icon != 0) {
|
||||
final UserHandle user = new UserHandle(userId);
|
||||
final StatusBarNotification n = new StatusBarNotification(
|
||||
pkg, id, tag, r.uid, r.initialPid, score, notification, user);
|
||||
if (old != null && old.statusBarKey != null) {
|
||||
@@ -1063,7 +1063,7 @@ public class NotificationManagerService extends INotificationManager.Stub
|
||||
try {
|
||||
final IRingtonePlayer player = mAudioService.getRingtonePlayer();
|
||||
if (player != null) {
|
||||
player.playAsync(uri, looping, audioStreamType);
|
||||
player.playAsync(uri, user, looping, audioStreamType);
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
} finally {
|
||||
|
||||
Reference in New Issue
Block a user