Merge "Better aidl generated dependency" into klp-dev
diff --git a/api/current.txt b/api/current.txt
index 936eb39..27a8480 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5500,10 +5500,13 @@
method public void onLowMemory();
method public void onTrimMemory(int);
method public android.content.res.AssetFileDescriptor openAssetFile(android.net.Uri, java.lang.String) throws java.io.FileNotFoundException;
+ method public android.content.res.AssetFileDescriptor openAssetFile(android.net.Uri, java.lang.String, android.os.CancellationSignal) throws java.io.FileNotFoundException;
method public android.os.ParcelFileDescriptor openFile(android.net.Uri, java.lang.String) throws java.io.FileNotFoundException;
+ method public android.os.ParcelFileDescriptor openFile(android.net.Uri, java.lang.String, android.os.CancellationSignal) throws java.io.FileNotFoundException;
method protected final android.os.ParcelFileDescriptor openFileHelper(android.net.Uri, java.lang.String) throws java.io.FileNotFoundException;
method public android.os.ParcelFileDescriptor openPipeHelper(android.net.Uri, java.lang.String, android.os.Bundle, T, android.content.ContentProvider.PipeDataWriter<T>) throws java.io.FileNotFoundException;
method public android.content.res.AssetFileDescriptor openTypedAssetFile(android.net.Uri, java.lang.String, android.os.Bundle) throws java.io.FileNotFoundException;
+ method public android.content.res.AssetFileDescriptor openTypedAssetFile(android.net.Uri, java.lang.String, android.os.Bundle, android.os.CancellationSignal) throws java.io.FileNotFoundException;
method public abstract android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
method public android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal);
method protected final void setPathPermissions(android.content.pm.PathPermission[]);
@@ -5527,8 +5530,11 @@
method public java.lang.String getType(android.net.Uri) throws android.os.RemoteException;
method public android.net.Uri insert(android.net.Uri, android.content.ContentValues) throws android.os.RemoteException;
method public android.content.res.AssetFileDescriptor openAssetFile(android.net.Uri, java.lang.String) throws java.io.FileNotFoundException, android.os.RemoteException;
+ method public android.content.res.AssetFileDescriptor openAssetFile(android.net.Uri, java.lang.String, android.os.CancellationSignal) throws java.io.FileNotFoundException, android.os.RemoteException;
method public android.os.ParcelFileDescriptor openFile(android.net.Uri, java.lang.String) throws java.io.FileNotFoundException, android.os.RemoteException;
+ method public android.os.ParcelFileDescriptor openFile(android.net.Uri, java.lang.String, android.os.CancellationSignal) throws java.io.FileNotFoundException, android.os.RemoteException;
method public final android.content.res.AssetFileDescriptor openTypedAssetFileDescriptor(android.net.Uri, java.lang.String, android.os.Bundle) throws java.io.FileNotFoundException, android.os.RemoteException;
+ method public final android.content.res.AssetFileDescriptor openTypedAssetFileDescriptor(android.net.Uri, java.lang.String, android.os.Bundle, android.os.CancellationSignal) throws java.io.FileNotFoundException, android.os.RemoteException;
method public android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String) throws android.os.RemoteException;
method public android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal) throws android.os.RemoteException;
method public boolean release();
@@ -5616,11 +5622,14 @@
method public void notifyChange(android.net.Uri, android.database.ContentObserver);
method public void notifyChange(android.net.Uri, android.database.ContentObserver, boolean);
method public final android.content.res.AssetFileDescriptor openAssetFileDescriptor(android.net.Uri, java.lang.String) throws java.io.FileNotFoundException;
+ method public final android.content.res.AssetFileDescriptor openAssetFileDescriptor(android.net.Uri, java.lang.String, android.os.CancellationSignal) throws java.io.FileNotFoundException;
method public final android.os.ParcelFileDescriptor openFileDescriptor(android.net.Uri, java.lang.String) throws java.io.FileNotFoundException;
+ method public final android.os.ParcelFileDescriptor openFileDescriptor(android.net.Uri, java.lang.String, android.os.CancellationSignal) throws java.io.FileNotFoundException;
method public final java.io.InputStream openInputStream(android.net.Uri) throws java.io.FileNotFoundException;
method public final java.io.OutputStream openOutputStream(android.net.Uri) throws java.io.FileNotFoundException;
method public final java.io.OutputStream openOutputStream(android.net.Uri, java.lang.String) throws java.io.FileNotFoundException;
method public final android.content.res.AssetFileDescriptor openTypedAssetFileDescriptor(android.net.Uri, java.lang.String, android.os.Bundle) throws java.io.FileNotFoundException;
+ method public final android.content.res.AssetFileDescriptor openTypedAssetFileDescriptor(android.net.Uri, java.lang.String, android.os.Bundle, android.os.CancellationSignal) throws java.io.FileNotFoundException;
method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal);
method public final void registerContentObserver(android.net.Uri, boolean, android.database.ContentObserver);
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index cf627d7..b9121c7 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -16,11 +16,9 @@
package android.content;
-import static android.content.pm.PackageManager.GET_PROVIDERS;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import android.app.AppOpsManager;
-import android.content.pm.PackageManager;
import android.content.pm.PathPermission;
import android.content.pm.ProviderInfo;
import android.content.res.AssetFileDescriptor;
@@ -261,17 +259,21 @@
}
@Override
- public ParcelFileDescriptor openFile(String callingPkg, Uri uri, String mode)
+ public ParcelFileDescriptor openFile(
+ String callingPkg, Uri uri, String mode, ICancellationSignal cancellationSignal)
throws FileNotFoundException {
enforceFilePermission(callingPkg, uri, mode);
- return ContentProvider.this.openFile(uri, mode);
+ return ContentProvider.this.openFile(
+ uri, mode, CancellationSignal.fromTransport(cancellationSignal));
}
@Override
- public AssetFileDescriptor openAssetFile(String callingPkg, Uri uri, String mode)
+ public AssetFileDescriptor openAssetFile(
+ String callingPkg, Uri uri, String mode, ICancellationSignal cancellationSignal)
throws FileNotFoundException {
enforceFilePermission(callingPkg, uri, mode);
- return ContentProvider.this.openAssetFile(uri, mode);
+ return ContentProvider.this.openAssetFile(
+ uri, mode, CancellationSignal.fromTransport(cancellationSignal));
}
@Override
@@ -286,9 +288,10 @@
@Override
public AssetFileDescriptor openTypedAssetFile(String callingPkg, Uri uri, String mimeType,
- Bundle opts) throws FileNotFoundException {
+ Bundle opts, ICancellationSignal cancellationSignal) throws FileNotFoundException {
enforceFilePermission(callingPkg, uri, "r");
- return ContentProvider.this.openTypedAssetFile(uri, mimeType, opts);
+ return ContentProvider.this.openTypedAssetFile(
+ uri, mimeType, opts, CancellationSignal.fromTransport(cancellationSignal));
}
@Override
@@ -874,6 +877,18 @@
* <p>The returned ParcelFileDescriptor is owned by the caller, so it is
* their responsibility to close it when done. That is, the implementation
* of this method should create a new ParcelFileDescriptor for each call.
+ * <p>
+ * If opened with the exclusive "r" or "w" modes, the returned
+ * ParcelFileDescriptor can be a pipe or socket pair to enable streaming
+ * of data. Opening with the "rw" or "rwt" modes implies a file on disk that
+ * supports seeking.
+ * <p>
+ * If you need to detect when the returned ParcelFileDescriptor has been
+ * closed, or if the remote process has crashed or encountered some other
+ * error, you can use {@link ParcelFileDescriptor#open(File, int,
+ * android.os.Handler, android.os.ParcelFileDescriptor.OnCloseListener)},
+ * {@link ParcelFileDescriptor#createReliablePipe()}, or
+ * {@link ParcelFileDescriptor#createReliableSocketPair()}.
*
* <p class="note">For use in Intents, you will want to implement {@link #getType}
* to return the appropriate MIME type for the data returned here with
@@ -911,6 +926,74 @@
}
/**
+ * Override this to handle requests to open a file blob.
+ * The default implementation always throws {@link FileNotFoundException}.
+ * This method can be called from multiple threads, as described in
+ * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes
+ * and Threads</a>.
+ *
+ * <p>This method returns a ParcelFileDescriptor, which is returned directly
+ * to the caller. This way large data (such as images and documents) can be
+ * returned without copying the content.
+ *
+ * <p>The returned ParcelFileDescriptor is owned by the caller, so it is
+ * their responsibility to close it when done. That is, the implementation
+ * of this method should create a new ParcelFileDescriptor for each call.
+ * <p>
+ * If opened with the exclusive "r" or "w" modes, the returned
+ * ParcelFileDescriptor can be a pipe or socket pair to enable streaming
+ * of data. Opening with the "rw" or "rwt" modes implies a file on disk that
+ * supports seeking.
+ * <p>
+ * If you need to detect when the returned ParcelFileDescriptor has been
+ * closed, or if the remote process has crashed or encountered some other
+ * error, you can use {@link ParcelFileDescriptor#open(File, int,
+ * android.os.Handler, android.os.ParcelFileDescriptor.OnCloseListener)},
+ * {@link ParcelFileDescriptor#createReliablePipe()}, or
+ * {@link ParcelFileDescriptor#createReliableSocketPair()}.
+ *
+ * <p class="note">For use in Intents, you will want to implement {@link #getType}
+ * to return the appropriate MIME type for the data returned here with
+ * the same URI. This will allow intent resolution to automatically determine the data MIME
+ * type and select the appropriate matching targets as part of its operation.</p>
+ *
+ * <p class="note">For better interoperability with other applications, it is recommended
+ * that for any URIs that can be opened, you also support queries on them
+ * containing at least the columns specified by {@link android.provider.OpenableColumns}.
+ * You may also want to support other common columns if you have additional meta-data
+ * to supply, such as {@link android.provider.MediaStore.MediaColumns#DATE_ADDED}
+ * in {@link android.provider.MediaStore.MediaColumns}.</p>
+ *
+ * @param uri The URI whose file is to be opened.
+ * @param mode Access mode for the file. May be "r" for read-only access,
+ * "w" for write-only access, "rw" for read and write access, or
+ * "rwt" for read and write access that truncates any existing
+ * file.
+ * @param signal A signal to cancel the operation in progress, or
+ * {@code null} if none. For example, if you are downloading a
+ * file from the network to service a "rw" mode request, you
+ * should periodically call
+ * {@link CancellationSignal#throwIfCanceled()} to check whether
+ * the client has canceled the request and abort the download.
+ *
+ * @return Returns a new ParcelFileDescriptor which you can use to access
+ * the file.
+ *
+ * @throws FileNotFoundException Throws FileNotFoundException if there is
+ * no file associated with the given URI or the mode is invalid.
+ * @throws SecurityException Throws SecurityException if the caller does
+ * not have permission to access the file.
+ *
+ * @see #openAssetFile(Uri, String)
+ * @see #openFileHelper(Uri, String)
+ * @see #getType(android.net.Uri)
+ */
+ public ParcelFileDescriptor openFile(Uri uri, String mode, CancellationSignal signal)
+ throws FileNotFoundException {
+ return openFile(uri, mode);
+ }
+
+ /**
* This is like {@link #openFile}, but can be implemented by providers
* that need to be able to return sub-sections of files, often assets
* inside of their .apk.
@@ -924,11 +1007,14 @@
* {@link ContentResolver#openInputStream ContentResolver.openInputStream}
* or {@link ContentResolver#openOutputStream ContentResolver.openOutputStream}
* methods.
+ * <p>
+ * The returned AssetFileDescriptor can be a pipe or socket pair to enable
+ * streaming of data.
*
* <p class="note">If you are implementing this to return a full file, you
* should create the AssetFileDescriptor with
* {@link AssetFileDescriptor#UNKNOWN_LENGTH} to be compatible with
- * applications that can not handle sub-sections of files.</p>
+ * applications that cannot handle sub-sections of files.</p>
*
* <p class="note">For use in Intents, you will want to implement {@link #getType}
* to return the appropriate MIME type for the data returned here with
@@ -965,6 +1051,68 @@
}
/**
+ * This is like {@link #openFile}, but can be implemented by providers
+ * that need to be able to return sub-sections of files, often assets
+ * inside of their .apk.
+ * This method can be called from multiple threads, as described in
+ * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes
+ * and Threads</a>.
+ *
+ * <p>If you implement this, your clients must be able to deal with such
+ * file slices, either directly with
+ * {@link ContentResolver#openAssetFileDescriptor}, or by using the higher-level
+ * {@link ContentResolver#openInputStream ContentResolver.openInputStream}
+ * or {@link ContentResolver#openOutputStream ContentResolver.openOutputStream}
+ * methods.
+ * <p>
+ * The returned AssetFileDescriptor can be a pipe or socket pair to enable
+ * streaming of data.
+ *
+ * <p class="note">If you are implementing this to return a full file, you
+ * should create the AssetFileDescriptor with
+ * {@link AssetFileDescriptor#UNKNOWN_LENGTH} to be compatible with
+ * applications that cannot handle sub-sections of files.</p>
+ *
+ * <p class="note">For use in Intents, you will want to implement {@link #getType}
+ * to return the appropriate MIME type for the data returned here with
+ * the same URI. This will allow intent resolution to automatically determine the data MIME
+ * type and select the appropriate matching targets as part of its operation.</p>
+ *
+ * <p class="note">For better interoperability with other applications, it is recommended
+ * that for any URIs that can be opened, you also support queries on them
+ * containing at least the columns specified by {@link android.provider.OpenableColumns}.</p>
+ *
+ * @param uri The URI whose file is to be opened.
+ * @param mode Access mode for the file. May be "r" for read-only access,
+ * "w" for write-only access (erasing whatever data is currently in
+ * the file), "wa" for write-only access to append to any existing data,
+ * "rw" for read and write access on any existing data, and "rwt" for read
+ * and write access that truncates any existing file.
+ * @param signal A signal to cancel the operation in progress, or
+ * {@code null} if none. For example, if you are downloading a
+ * file from the network to service a "rw" mode request, you
+ * should periodically call
+ * {@link CancellationSignal#throwIfCanceled()} to check whether
+ * the client has canceled the request and abort the download.
+ *
+ * @return Returns a new AssetFileDescriptor which you can use to access
+ * the file.
+ *
+ * @throws FileNotFoundException Throws FileNotFoundException if there is
+ * no file associated with the given URI or the mode is invalid.
+ * @throws SecurityException Throws SecurityException if the caller does
+ * not have permission to access the file.
+ *
+ * @see #openFile(Uri, String)
+ * @see #openFileHelper(Uri, String)
+ * @see #getType(android.net.Uri)
+ */
+ public AssetFileDescriptor openAssetFile(Uri uri, String mode, CancellationSignal signal)
+ throws FileNotFoundException {
+ return openAssetFile(uri, mode);
+ }
+
+ /**
* Convenience for subclasses that wish to implement {@link #openFile}
* by looking up a column named "_data" at the given URI.
*
@@ -1041,6 +1189,9 @@
*
* <p>See {@link ClipData} for examples of the use and implementation
* of this method.
+ * <p>
+ * The returned AssetFileDescriptor can be a pipe or socket pair to enable
+ * streaming of data.
*
* <p class="note">For better interoperability with other applications, it is recommended
* that for any URIs that can be opened, you also support queries on them
@@ -1086,6 +1237,64 @@
throw new FileNotFoundException("Can't open " + uri + " as type " + mimeTypeFilter);
}
+
+ /**
+ * Called by a client to open a read-only stream containing data of a
+ * particular MIME type. This is like {@link #openAssetFile(Uri, String)},
+ * except the file can only be read-only and the content provider may
+ * perform data conversions to generate data of the desired type.
+ *
+ * <p>The default implementation compares the given mimeType against the
+ * result of {@link #getType(Uri)} and, if they match, simply calls
+ * {@link #openAssetFile(Uri, String)}.
+ *
+ * <p>See {@link ClipData} for examples of the use and implementation
+ * of this method.
+ * <p>
+ * The returned AssetFileDescriptor can be a pipe or socket pair to enable
+ * streaming of data.
+ *
+ * <p class="note">For better interoperability with other applications, it is recommended
+ * that for any URIs that can be opened, you also support queries on them
+ * containing at least the columns specified by {@link android.provider.OpenableColumns}.
+ * You may also want to support other common columns if you have additional meta-data
+ * to supply, such as {@link android.provider.MediaStore.MediaColumns#DATE_ADDED}
+ * in {@link android.provider.MediaStore.MediaColumns}.</p>
+ *
+ * @param uri The data in the content provider being queried.
+ * @param mimeTypeFilter The type of data the client desires. May be
+ * a pattern, such as *\/*, if the caller does not have specific type
+ * requirements; in this case the content provider will pick its best
+ * type matching the pattern.
+ * @param opts Additional options from the client. The definitions of
+ * these are specific to the content provider being called.
+ * @param signal A signal to cancel the operation in progress, or
+ * {@code null} if none. For example, if you are downloading a
+ * file from the network to service a "rw" mode request, you
+ * should periodically call
+ * {@link CancellationSignal#throwIfCanceled()} to check whether
+ * the client has canceled the request and abort the download.
+ *
+ * @return Returns a new AssetFileDescriptor from which the client can
+ * read data of the desired type.
+ *
+ * @throws FileNotFoundException Throws FileNotFoundException if there is
+ * no file associated with the given URI or the mode is invalid.
+ * @throws SecurityException Throws SecurityException if the caller does
+ * not have permission to access the data.
+ * @throws IllegalArgumentException Throws IllegalArgumentException if the
+ * content provider does not support the requested MIME type.
+ *
+ * @see #getStreamTypes(Uri, String)
+ * @see #openAssetFile(Uri, String)
+ * @see ClipDescription#compareMimeTypes(String, String)
+ */
+ public AssetFileDescriptor openTypedAssetFile(
+ Uri uri, String mimeTypeFilter, Bundle opts, CancellationSignal signal)
+ throws FileNotFoundException {
+ return openTypedAssetFile(uri, mimeTypeFilter, opts);
+ }
+
/**
* Interface to write a stream of data to a pipe. Use with
* {@link ContentProvider#openPipeHelper}.
diff --git a/core/java/android/content/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java
index 8dffac7..024a521 100644
--- a/core/java/android/content/ContentProviderClient.java
+++ b/core/java/android/content/ContentProviderClient.java
@@ -63,14 +63,7 @@
/** See {@link ContentProvider#query ContentProvider.query} */
public Cursor query(Uri url, String[] projection, String selection,
String[] selectionArgs, String sortOrder) throws RemoteException {
- try {
- return query(url, projection, selection, selectionArgs, sortOrder, null);
- } catch (DeadObjectException e) {
- if (!mStable) {
- mContentResolver.unstableProviderDied(mContentProvider);
- }
- throw e;
- }
+ return query(url, projection, selection, selectionArgs, sortOrder, null);
}
/** See {@link ContentProvider#query ContentProvider.query} */
@@ -177,8 +170,25 @@
*/
public ParcelFileDescriptor openFile(Uri url, String mode)
throws RemoteException, FileNotFoundException {
+ return openFile(url, mode, null);
+ }
+
+ /**
+ * See {@link ContentProvider#openFile ContentProvider.openFile}. Note that
+ * this <em>does not</em>
+ * take care of non-content: URIs such as file:. It is strongly recommended
+ * you use the {@link ContentResolver#openFileDescriptor
+ * ContentResolver.openFileDescriptor} API instead.
+ */
+ public ParcelFileDescriptor openFile(Uri url, String mode, CancellationSignal signal)
+ throws RemoteException, FileNotFoundException {
+ ICancellationSignal remoteSignal = null;
+ if (signal != null) {
+ remoteSignal = mContentProvider.createCancellationSignal();
+ signal.setRemote(remoteSignal);
+ }
try {
- return mContentProvider.openFile(mPackageName, url, mode);
+ return mContentProvider.openFile(mPackageName, url, mode, remoteSignal);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -196,8 +206,25 @@
*/
public AssetFileDescriptor openAssetFile(Uri url, String mode)
throws RemoteException, FileNotFoundException {
+ return openAssetFile(url, mode, null);
+ }
+
+ /**
+ * See {@link ContentProvider#openAssetFile ContentProvider.openAssetFile}.
+ * Note that this <em>does not</em>
+ * take care of non-content: URIs such as file:. It is strongly recommended
+ * you use the {@link ContentResolver#openAssetFileDescriptor
+ * ContentResolver.openAssetFileDescriptor} API instead.
+ */
+ public AssetFileDescriptor openAssetFile(Uri url, String mode, CancellationSignal signal)
+ throws RemoteException, FileNotFoundException {
+ ICancellationSignal remoteSignal = null;
+ if (signal != null) {
+ remoteSignal = mContentProvider.createCancellationSignal();
+ signal.setRemote(remoteSignal);
+ }
try {
- return mContentProvider.openAssetFile(mPackageName, url, mode);
+ return mContentProvider.openAssetFile(mPackageName, url, mode, remoteSignal);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -208,10 +235,22 @@
/** See {@link ContentProvider#openTypedAssetFile ContentProvider.openTypedAssetFile} */
public final AssetFileDescriptor openTypedAssetFileDescriptor(Uri uri,
- String mimeType, Bundle opts)
+ String mimeType, Bundle opts) throws RemoteException, FileNotFoundException {
+ return openTypedAssetFileDescriptor(uri, mimeType, opts, null);
+ }
+
+ /** See {@link ContentProvider#openTypedAssetFile ContentProvider.openTypedAssetFile} */
+ public final AssetFileDescriptor openTypedAssetFileDescriptor(Uri uri,
+ String mimeType, Bundle opts, CancellationSignal signal)
throws RemoteException, FileNotFoundException {
+ ICancellationSignal remoteSignal = null;
+ if (signal != null) {
+ remoteSignal = mContentProvider.createCancellationSignal();
+ signal.setRemote(remoteSignal);
+ }
try {
- return mContentProvider.openTypedAssetFile(mPackageName, uri, mimeType, opts);
+ return mContentProvider.openTypedAssetFile(
+ mPackageName, uri, mimeType, opts, remoteSignal);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java
index 6f822c1..744e68c 100644
--- a/core/java/android/content/ContentProviderNative.java
+++ b/core/java/android/content/ContentProviderNative.java
@@ -18,22 +18,20 @@
import android.content.res.AssetFileDescriptor;
import android.database.BulkCursorDescriptor;
-import android.database.BulkCursorNative;
import android.database.BulkCursorToCursorAdaptor;
import android.database.Cursor;
import android.database.CursorToBulkCursorAdaptor;
import android.database.DatabaseUtils;
-import android.database.IBulkCursor;
import android.database.IContentObserver;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
-import android.os.RemoteException;
import android.os.IBinder;
import android.os.ICancellationSignal;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
+import android.os.RemoteException;
import java.io.FileNotFoundException;
import java.util.ArrayList;
@@ -227,9 +225,11 @@
String callingPkg = data.readString();
Uri url = Uri.CREATOR.createFromParcel(data);
String mode = data.readString();
+ ICancellationSignal signal = ICancellationSignal.Stub.asInterface(
+ data.readStrongBinder());
ParcelFileDescriptor fd;
- fd = openFile(callingPkg, url, mode);
+ fd = openFile(callingPkg, url, mode, signal);
reply.writeNoException();
if (fd != null) {
reply.writeInt(1);
@@ -247,9 +247,11 @@
String callingPkg = data.readString();
Uri url = Uri.CREATOR.createFromParcel(data);
String mode = data.readString();
+ ICancellationSignal signal = ICancellationSignal.Stub.asInterface(
+ data.readStrongBinder());
AssetFileDescriptor fd;
- fd = openAssetFile(callingPkg, url, mode);
+ fd = openAssetFile(callingPkg, url, mode, signal);
reply.writeNoException();
if (fd != null) {
reply.writeInt(1);
@@ -296,9 +298,11 @@
Uri url = Uri.CREATOR.createFromParcel(data);
String mimeType = data.readString();
Bundle opts = data.readBundle();
+ ICancellationSignal signal = ICancellationSignal.Stub.asInterface(
+ data.readStrongBinder());
AssetFileDescriptor fd;
- fd = openTypedAssetFile(callingPkg, url, mimeType, opts);
+ fd = openTypedAssetFile(callingPkg, url, mimeType, opts, signal);
reply.writeNoException();
if (fd != null) {
reply.writeInt(1);
@@ -538,7 +542,9 @@
}
}
- public ParcelFileDescriptor openFile(String callingPkg, Uri url, String mode)
+ @Override
+ public ParcelFileDescriptor openFile(
+ String callingPkg, Uri url, String mode, ICancellationSignal signal)
throws RemoteException, FileNotFoundException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
@@ -548,6 +554,7 @@
data.writeString(callingPkg);
url.writeToParcel(data, 0);
data.writeString(mode);
+ data.writeStrongBinder(signal != null ? signal.asBinder() : null);
mRemote.transact(IContentProvider.OPEN_FILE_TRANSACTION, data, reply, 0);
@@ -561,7 +568,9 @@
}
}
- public AssetFileDescriptor openAssetFile(String callingPkg, Uri url, String mode)
+ @Override
+ public AssetFileDescriptor openAssetFile(
+ String callingPkg, Uri url, String mode, ICancellationSignal signal)
throws RemoteException, FileNotFoundException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
@@ -571,6 +580,7 @@
data.writeString(callingPkg);
url.writeToParcel(data, 0);
data.writeString(mode);
+ data.writeStrongBinder(signal != null ? signal.asBinder() : null);
mRemote.transact(IContentProvider.OPEN_ASSET_FILE_TRANSACTION, data, reply, 0);
@@ -629,8 +639,9 @@
}
}
+ @Override
public AssetFileDescriptor openTypedAssetFile(String callingPkg, Uri url, String mimeType,
- Bundle opts) throws RemoteException, FileNotFoundException {
+ Bundle opts, ICancellationSignal signal) throws RemoteException, FileNotFoundException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
@@ -640,6 +651,7 @@
url.writeToParcel(data, 0);
data.writeString(mimeType);
data.writeBundle(opts);
+ data.writeStrongBinder(signal != null ? signal.asBinder() : null);
mRemote.transact(IContentProvider.OPEN_TYPED_ASSET_FILE_TRANSACTION, data, reply, 0);
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 5cabfeef..a761a89 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -55,7 +55,6 @@
import java.util.List;
import java.util.Random;
-
/**
* This class provides applications access to the content model.
*
@@ -328,7 +327,7 @@
* content URL can be returned when opened as as stream with
* {@link #openTypedAssetFileDescriptor}. Note that the types here are
* not necessarily a superset of the type returned by {@link #getType} --
- * many content providers can not return a raw stream for the structured
+ * many content providers cannot return a raw stream for the structured
* data that they contain.
*
* @param url A Uri identifying content (either a list or specific type),
@@ -485,6 +484,9 @@
if (qCursor != null) {
qCursor.close();
}
+ if (cancellationSignal != null) {
+ cancellationSignal.setRemote(null);
+ }
if (unstableProvider != null) {
releaseUnstableProvider(unstableProvider);
}
@@ -531,7 +533,7 @@
// with sufficient testing.
return new FileInputStream(uri.getPath());
} else {
- AssetFileDescriptor fd = openAssetFileDescriptor(uri, "r");
+ AssetFileDescriptor fd = openAssetFileDescriptor(uri, "r", null);
try {
return fd != null ? fd.createInputStream() : null;
} catch (IOException e) {
@@ -571,7 +573,7 @@
*/
public final OutputStream openOutputStream(Uri uri, String mode)
throws FileNotFoundException {
- AssetFileDescriptor fd = openAssetFileDescriptor(uri, mode);
+ AssetFileDescriptor fd = openAssetFileDescriptor(uri, mode, null);
try {
return fd != null ? fd.createOutputStream() : null;
} catch (IOException e) {
@@ -597,6 +599,15 @@
*
* <p>See {@link #openAssetFileDescriptor(Uri, String)} for more information
* on these schemes.
+ * <p>
+ * If opening with the exclusive "r" or "w" modes, the returned
+ * ParcelFileDescriptor could be a pipe or socket pair to enable streaming
+ * of data. Opening with the "rw" mode implies a file on disk that supports
+ * seeking. If possible, always use an exclusive mode to give the underlying
+ * {@link ContentProvider} the most flexibility.
+ * <p>
+ * If you are writing a file, and need to communicate an error to the
+ * provider, use {@link ParcelFileDescriptor#closeWithError(String)}.
*
* @param uri The desired URI to open.
* @param mode The file mode to use, as per {@link ContentProvider#openFile
@@ -607,9 +618,54 @@
* file exists under the URI or the mode is invalid.
* @see #openAssetFileDescriptor(Uri, String)
*/
+ public final ParcelFileDescriptor openFileDescriptor(Uri uri, String mode)
+ throws FileNotFoundException {
+ return openFileDescriptor(uri, mode, null);
+ }
+
+ /**
+ * Open a raw file descriptor to access data under a URI. This
+ * is like {@link #openAssetFileDescriptor(Uri, String)}, but uses the
+ * underlying {@link ContentProvider#openFile}
+ * ContentProvider.openFile()} method, so will <em>not</em> work with
+ * providers that return sub-sections of files. If at all possible,
+ * you should use {@link #openAssetFileDescriptor(Uri, String)}. You
+ * will receive a FileNotFoundException exception if the provider returns a
+ * sub-section of a file.
+ *
+ * <h5>Accepts the following URI schemes:</h5>
+ * <ul>
+ * <li>content ({@link #SCHEME_CONTENT})</li>
+ * <li>file ({@link #SCHEME_FILE})</li>
+ * </ul>
+ *
+ * <p>See {@link #openAssetFileDescriptor(Uri, String)} for more information
+ * on these schemes.
+ * <p>
+ * If opening with the exclusive "r" or "w" modes, the returned
+ * ParcelFileDescriptor could be a pipe or socket pair to enable streaming
+ * of data. Opening with the "rw" mode implies a file on disk that supports
+ * seeking. If possible, always use an exclusive mode to give the underlying
+ * {@link ContentProvider} the most flexibility.
+ * <p>
+ * If you are writing a file, and need to communicate an error to the
+ * provider, use {@link ParcelFileDescriptor#closeWithError(String)}.
+ *
+ * @param uri The desired URI to open.
+ * @param mode The file mode to use, as per {@link ContentProvider#openFile
+ * ContentProvider.openFile}.
+ * @param cancellationSignal A signal to cancel the operation in progress,
+ * or null if none. If the operation is canceled, then
+ * {@link OperationCanceledException} will be thrown.
+ * @return Returns a new ParcelFileDescriptor pointing to the file. You
+ * own this descriptor and are responsible for closing it when done.
+ * @throws FileNotFoundException Throws FileNotFoundException if no
+ * file exists under the URI or the mode is invalid.
+ * @see #openAssetFileDescriptor(Uri, String)
+ */
public final ParcelFileDescriptor openFileDescriptor(Uri uri,
- String mode) throws FileNotFoundException {
- AssetFileDescriptor afd = openAssetFileDescriptor(uri, mode);
+ String mode, CancellationSignal cancellationSignal) throws FileNotFoundException {
+ AssetFileDescriptor afd = openAssetFileDescriptor(uri, mode, cancellationSignal);
if (afd == null) {
return null;
}
@@ -677,8 +733,64 @@
* @throws FileNotFoundException Throws FileNotFoundException of no
* file exists under the URI or the mode is invalid.
*/
+ public final AssetFileDescriptor openAssetFileDescriptor(Uri uri, String mode)
+ throws FileNotFoundException {
+ return openAssetFileDescriptor(uri, mode, null);
+ }
+
+ /**
+ * Open a raw file descriptor to access data under a URI. This
+ * interacts with the underlying {@link ContentProvider#openAssetFile}
+ * method of the provider associated with the given URI, to retrieve any file stored there.
+ *
+ * <h5>Accepts the following URI schemes:</h5>
+ * <ul>
+ * <li>content ({@link #SCHEME_CONTENT})</li>
+ * <li>android.resource ({@link #SCHEME_ANDROID_RESOURCE})</li>
+ * <li>file ({@link #SCHEME_FILE})</li>
+ * </ul>
+ * <h5>The android.resource ({@link #SCHEME_ANDROID_RESOURCE}) Scheme</h5>
+ * <p>
+ * A Uri object can be used to reference a resource in an APK file. The
+ * Uri should be one of the following formats:
+ * <ul>
+ * <li><code>android.resource://package_name/id_number</code><br/>
+ * <code>package_name</code> is your package name as listed in your AndroidManifest.xml.
+ * For example <code>com.example.myapp</code><br/>
+ * <code>id_number</code> is the int form of the ID.<br/>
+ * The easiest way to construct this form is
+ * <pre>Uri uri = Uri.parse("android.resource://com.example.myapp/" + R.raw.my_resource");</pre>
+ * </li>
+ * <li><code>android.resource://package_name/type/name</code><br/>
+ * <code>package_name</code> is your package name as listed in your AndroidManifest.xml.
+ * For example <code>com.example.myapp</code><br/>
+ * <code>type</code> is the string form of the resource type. For example, <code>raw</code>
+ * or <code>drawable</code>.
+ * <code>name</code> is the string form of the resource name. That is, whatever the file
+ * name was in your res directory, without the type extension.
+ * The easiest way to construct this form is
+ * <pre>Uri uri = Uri.parse("android.resource://com.example.myapp/raw/my_resource");</pre>
+ * </li>
+ * </ul>
+ *
+ * <p>Note that if this function is called for read-only input (mode is "r")
+ * on a content: URI, it will instead call {@link #openTypedAssetFileDescriptor}
+ * for you with a MIME type of "*\/*". This allows such callers to benefit
+ * from any built-in data conversion that a provider implements.
+ *
+ * @param uri The desired URI to open.
+ * @param mode The file mode to use, as per {@link ContentProvider#openAssetFile
+ * ContentProvider.openAssetFile}.
+ * @param cancellationSignal A signal to cancel the operation in progress, or null if
+ * none. If the operation is canceled, then
+ * {@link OperationCanceledException} will be thrown.
+ * @return Returns a new ParcelFileDescriptor pointing to the file. You
+ * own this descriptor and are responsible for closing it when done.
+ * @throws FileNotFoundException Throws FileNotFoundException of no
+ * file exists under the URI or the mode is invalid.
+ */
public final AssetFileDescriptor openAssetFileDescriptor(Uri uri,
- String mode) throws FileNotFoundException {
+ String mode, CancellationSignal cancellationSignal) throws FileNotFoundException {
String scheme = uri.getScheme();
if (SCHEME_ANDROID_RESOURCE.equals(scheme)) {
if (!"r".equals(mode)) {
@@ -696,7 +808,7 @@
return new AssetFileDescriptor(pfd, 0, -1);
} else {
if ("r".equals(mode)) {
- return openTypedAssetFileDescriptor(uri, "*/*", null);
+ return openTypedAssetFileDescriptor(uri, "*/*", null, cancellationSignal);
} else {
IContentProvider unstableProvider = acquireUnstableProvider(uri);
if (unstableProvider == null) {
@@ -706,8 +818,16 @@
AssetFileDescriptor fd = null;
try {
+ ICancellationSignal remoteCancellationSignal = null;
+ if (cancellationSignal != null) {
+ cancellationSignal.throwIfCanceled();
+ remoteCancellationSignal = unstableProvider.createCancellationSignal();
+ cancellationSignal.setRemote(remoteCancellationSignal);
+ }
+
try {
- fd = unstableProvider.openAssetFile(mPackageName, uri, mode);
+ fd = unstableProvider.openAssetFile(
+ mPackageName, uri, mode, remoteCancellationSignal);
if (fd == null) {
// The provider will be released by the finally{} clause
return null;
@@ -721,7 +841,8 @@
if (stableProvider == null) {
throw new FileNotFoundException("No content provider: " + uri);
}
- fd = stableProvider.openAssetFile(mPackageName, uri, mode);
+ fd = stableProvider.openAssetFile(
+ mPackageName, uri, mode, remoteCancellationSignal);
if (fd == null) {
// The provider will be released by the finally{} clause
return null;
@@ -749,6 +870,9 @@
} catch (FileNotFoundException e) {
throw e;
} finally {
+ if (cancellationSignal != null) {
+ cancellationSignal.setRemote(null);
+ }
if (stableProvider != null) {
releaseProvider(stableProvider);
}
@@ -788,8 +912,45 @@
* @throws FileNotFoundException Throws FileNotFoundException of no
* data of the desired type exists under the URI.
*/
+ public final AssetFileDescriptor openTypedAssetFileDescriptor(
+ Uri uri, String mimeType, Bundle opts) throws FileNotFoundException {
+ return openTypedAssetFileDescriptor(uri, mimeType, opts, null);
+ }
+
+ /**
+ * Open a raw file descriptor to access (potentially type transformed)
+ * data from a "content:" URI. This interacts with the underlying
+ * {@link ContentProvider#openTypedAssetFile} method of the provider
+ * associated with the given URI, to retrieve retrieve any appropriate
+ * data stream for the data stored there.
+ *
+ * <p>Unlike {@link #openAssetFileDescriptor}, this function only works
+ * with "content:" URIs, because content providers are the only facility
+ * with an associated MIME type to ensure that the returned data stream
+ * is of the desired type.
+ *
+ * <p>All text/* streams are encoded in UTF-8.
+ *
+ * @param uri The desired URI to open.
+ * @param mimeType The desired MIME type of the returned data. This can
+ * be a pattern such as *\/*, which will allow the content provider to
+ * select a type, though there is no way for you to determine what type
+ * it is returning.
+ * @param opts Additional provider-dependent options.
+ * @param cancellationSignal A signal to cancel the operation in progress,
+ * or null if none. If the operation is canceled, then
+ * {@link OperationCanceledException} will be thrown.
+ * @return Returns a new ParcelFileDescriptor from which you can read the
+ * data stream from the provider. Note that this may be a pipe, meaning
+ * you can't seek in it. The only seek you should do is if the
+ * AssetFileDescriptor contains an offset, to move to that offset before
+ * reading. You own this descriptor and are responsible for closing it when done.
+ * @throws FileNotFoundException Throws FileNotFoundException of no
+ * data of the desired type exists under the URI.
+ */
public final AssetFileDescriptor openTypedAssetFileDescriptor(Uri uri,
- String mimeType, Bundle opts) throws FileNotFoundException {
+ String mimeType, Bundle opts, CancellationSignal cancellationSignal)
+ throws FileNotFoundException {
IContentProvider unstableProvider = acquireUnstableProvider(uri);
if (unstableProvider == null) {
throw new FileNotFoundException("No content provider: " + uri);
@@ -798,8 +959,16 @@
AssetFileDescriptor fd = null;
try {
+ ICancellationSignal remoteCancellationSignal = null;
+ if (cancellationSignal != null) {
+ cancellationSignal.throwIfCanceled();
+ remoteCancellationSignal = unstableProvider.createCancellationSignal();
+ cancellationSignal.setRemote(remoteCancellationSignal);
+ }
+
try {
- fd = unstableProvider.openTypedAssetFile(mPackageName, uri, mimeType, opts);
+ fd = unstableProvider.openTypedAssetFile(
+ mPackageName, uri, mimeType, opts, remoteCancellationSignal);
if (fd == null) {
// The provider will be released by the finally{} clause
return null;
@@ -813,7 +982,8 @@
if (stableProvider == null) {
throw new FileNotFoundException("No content provider: " + uri);
}
- fd = stableProvider.openTypedAssetFile(mPackageName, uri, mimeType, opts);
+ fd = stableProvider.openTypedAssetFile(
+ mPackageName, uri, mimeType, opts, remoteCancellationSignal);
if (fd == null) {
// The provider will be released by the finally{} clause
return null;
@@ -841,6 +1011,9 @@
} catch (FileNotFoundException e) {
throw e;
} finally {
+ if (cancellationSignal != null) {
+ cancellationSignal.setRemote(null);
+ }
if (stableProvider != null) {
releaseProvider(stableProvider);
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index ff81f72..cd1f87b 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2375,7 +2375,7 @@
/**
* {@link android.print.PrintManager} for printing and managing
- * printers and print taks.
+ * printers and print tasks.
*
* @see #getSystemService
* @see android.print.PrintManager
diff --git a/core/java/android/content/IContentProvider.java b/core/java/android/content/IContentProvider.java
index 62b79f0..6ea8876 100644
--- a/core/java/android/content/IContentProvider.java
+++ b/core/java/android/content/IContentProvider.java
@@ -46,9 +46,11 @@
throws RemoteException;
public int update(String callingPkg, Uri url, ContentValues values, String selection,
String[] selectionArgs) throws RemoteException;
- public ParcelFileDescriptor openFile(String callingPkg, Uri url, String mode)
+ public ParcelFileDescriptor openFile(
+ String callingPkg, Uri url, String mode, ICancellationSignal signal)
throws RemoteException, FileNotFoundException;
- public AssetFileDescriptor openAssetFile(String callingPkg, Uri url, String mode)
+ public AssetFileDescriptor openAssetFile(
+ String callingPkg, Uri url, String mode, ICancellationSignal signal)
throws RemoteException, FileNotFoundException;
public ContentProviderResult[] applyBatch(String callingPkg,
ArrayList<ContentProviderOperation> operations)
@@ -60,7 +62,7 @@
// Data interchange.
public String[] getStreamTypes(Uri url, String mimeTypeFilter) throws RemoteException;
public AssetFileDescriptor openTypedAssetFile(String callingPkg, Uri url, String mimeType,
- Bundle opts) throws RemoteException, FileNotFoundException;
+ Bundle opts, ICancellationSignal signal) throws RemoteException, FileNotFoundException;
/* IPC constants */
static final String descriptor = "android.content.IContentProvider";
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 017ad98..c99f09c 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -35,6 +35,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.os.StrictMode;
+import android.provider.DocumentsContract;
import android.util.AttributeSet;
import android.util.Log;
@@ -1192,7 +1193,7 @@
/**
* An optional field on {@link #ACTION_ASSIST} and {@link #ACTION_VOICE_ASSIST}
- * containing an the names of the application package of foreground services at the time
+ * containing the application package names of foreground services at the time
* of the assist request. This is an array of {@link String}s, with one entry
* per service.
*/
@@ -2650,11 +2651,16 @@
* multiple selection), then it can specify {@link #EXTRA_ALLOW_MULTIPLE} to
* indicate this.
* <p>
- * All returned URIs can be opened as a stream with
- * {@link ContentResolver#openInputStream(Uri)}.
+ * Callers must include {@link #CATEGORY_OPENABLE} in the Intent so that
+ * returned URIs can be opened with
+ * {@link ContentResolver#openFileDescriptor(Uri, String)}.
* <p>
* Output: The URI of the item that was picked. This must be a content: URI
- * so that any receiver can access it.
+ * so that any receiver can access it. If multiple documents were selected,
+ * they are returned in {@link #getClipData()}.
+ *
+ * @see DocumentsContract
+ * @see DocumentsContract#getOpenDocuments(Context)
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_OPEN_DOCUMENT = "android.intent.action.OPEN_DOCUMENT";
@@ -2669,11 +2675,14 @@
* optionally hint at the MIME type being created by setting
* {@link #setType(String)}.
* <p>
- * All returned URIs can be opened as a stream with
- * {@link ContentResolver#openOutputStream(Uri)}.
+ * Callers must include {@link #CATEGORY_OPENABLE} in the Intent so that
+ * returned URIs can be opened with
+ * {@link ContentResolver#openFileDescriptor(Uri, String)}.
* <p>
* Output: The URI of the item that was created. This must be a content: URI
* so that any receiver can access it.
+ *
+ * @see DocumentsContract
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_CREATE_DOCUMENT = "android.intent.action.CREATE_DOCUMENT";
diff --git a/core/java/android/database/Cursor.java b/core/java/android/database/Cursor.java
index 7381e2c..fc2a885 100644
--- a/core/java/android/database/Cursor.java
+++ b/core/java/android/database/Cursor.java
@@ -25,9 +25,12 @@
/**
* This interface provides random read-write access to the result set returned
* by a database query.
- *
+ * <p>
* Cursor implementations are not required to be synchronized so code using a Cursor from multiple
* threads should perform its own synchronization when using the Cursor.
+ * </p><p>
+ * Implementations should subclass {@link AbstractCursor}.
+ * </p>
*/
public interface Cursor extends Closeable {
/*
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 7d65736..f12be5f 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -74,8 +74,7 @@
* richer second screen experiences.
* </p>
*
- * @see android.app.Presentation for information about presenting content
- * on secondary displays.
+ * @see android.app.Presentation
* @see Display#FLAG_PRESENTATION
* @see #getDisplays(String)
*/
@@ -138,8 +137,7 @@
* more special-purpose displays.
* </p>
*
- * @see android.app.Presentation for information about presenting content
- * on secondary displays.
+ * @see android.app.Presentation
* @see #createVirtualDisplay
* @see #DISPLAY_CATEGORY_PRESENTATION
* @see Display#FLAG_PRESENTATION
@@ -168,7 +166,7 @@
* The content of secure windows will be blanked if shown on this display.
* </p>
*
- * @see Display#FLAG_SECURE for information about secure displays.
+ * @see Display#FLAG_SECURE
* @see #createVirtualDisplay
*/
public static final int VIRTUAL_DISPLAY_FLAG_SECURE = 1 << 2;
@@ -325,6 +323,16 @@
mGlobal.connectWifiDisplay(deviceAddress);
}
+ /** @hide */
+ public void pauseWifiDisplay() {
+ mGlobal.pauseWifiDisplay();
+ }
+
+ /** @hide */
+ public void resumeWifiDisplay() {
+ mGlobal.resumeWifiDisplay();
+ }
+
/**
* Disconnects from the current Wifi display.
* The results are sent as a {@link #ACTION_WIFI_DISPLAY_STATUS_CHANGED} broadcast.
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 10c14ff..936a086 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -287,6 +287,22 @@
}
}
+ public void pauseWifiDisplay() {
+ try {
+ mDm.pauseWifiDisplay();
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Failed to pause Wifi display.", ex);
+ }
+ }
+
+ public void resumeWifiDisplay() {
+ try {
+ mDm.resumeWifiDisplay();
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Failed to resume Wifi display.", ex);
+ }
+ }
+
public void disconnectWifiDisplay() {
try {
mDm.disconnectWifiDisplay();
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index afaf436..6b2c887 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -55,4 +55,10 @@
// No permissions required but must be same Uid as the creator.
void releaseVirtualDisplay(in IBinder token);
+
+ // Requires CONFIGURE_WIFI_DISPLAY permission.
+ void pauseWifiDisplay();
+
+ // Requires CONFIGURE_WIFI_DISPLAY permission.
+ void resumeWifiDisplay();
}
diff --git a/core/java/android/hardware/display/WifiDisplaySessionInfo.java b/core/java/android/hardware/display/WifiDisplaySessionInfo.java
new file mode 100644
index 0000000..33d2725
--- /dev/null
+++ b/core/java/android/hardware/display/WifiDisplaySessionInfo.java
@@ -0,0 +1,116 @@
+/*
+ * 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.hardware.display;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * This class contains information regarding a wifi display session
+ * (such as session id, source ip address, etc.). This is needed for
+ * Wifi Display Certification process.
+ * <p>
+ * This object is immutable.
+ * </p>
+ *
+ * @hide
+ */
+public final class WifiDisplaySessionInfo implements Parcelable {
+ private final boolean mClient;
+ private final int mSessionId;
+ private final String mGroupId;
+ private final String mPassphrase;
+ private final String mIP;
+
+ public static final Creator<WifiDisplaySessionInfo> CREATOR =
+ new Creator<WifiDisplaySessionInfo>() {
+ @Override
+ public WifiDisplaySessionInfo createFromParcel(Parcel in) {
+ boolean client = (in.readInt() != 0);
+ int session = in.readInt();
+ String group = in.readString();
+ String pp = in.readString();
+ String ip = in.readString();
+
+ return new WifiDisplaySessionInfo(client, session, group, pp, ip);
+ }
+
+ @Override
+ public WifiDisplaySessionInfo[] newArray(int size) {
+ return new WifiDisplaySessionInfo[size];
+ }
+ };
+
+ public WifiDisplaySessionInfo() {
+ this(true, 0, "", "", "");
+ }
+
+ public WifiDisplaySessionInfo(
+ boolean client, int session, String group, String pp, String ip) {
+ mClient = client;
+ mSessionId = session;
+ mGroupId = group;
+ mPassphrase = pp;
+ mIP = ip;
+ }
+
+ public boolean isClient() {
+ return mClient;
+ }
+
+ public int getSessionId() {
+ return mSessionId;
+ }
+
+ public String getGroupId() {
+ return mGroupId;
+ }
+
+ public String getPassphrase() {
+ return mPassphrase;
+ }
+
+ public String getIP() {
+ return mIP;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mClient ? 1 : 0);
+ dest.writeInt(mSessionId);
+ dest.writeString(mGroupId);
+ dest.writeString(mPassphrase);
+ dest.writeString(mIP);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ // For debugging purposes only.
+ @Override
+ public String toString() {
+ return "WifiDisplaySessionInfo:"
+ +"\n Client/Owner: " + (mClient ? "Client":"Owner")
+ +"\n GroupId: " + mGroupId
+ +"\n Passphrase: " + mPassphrase
+ +"\n SessionId: " + mSessionId
+ +"\n IP Address: " + mIP
+ ;
+ }
+}
diff --git a/core/java/android/hardware/display/WifiDisplayStatus.java b/core/java/android/hardware/display/WifiDisplayStatus.java
index 77acdc0..5216727 100644
--- a/core/java/android/hardware/display/WifiDisplayStatus.java
+++ b/core/java/android/hardware/display/WifiDisplayStatus.java
@@ -39,6 +39,9 @@
private final WifiDisplay mActiveDisplay;
private final WifiDisplay[] mDisplays;
+ /** Session info needed for Miracast Certification */
+ private final WifiDisplaySessionInfo mSessionInfo;
+
/** Feature state: Wifi display is not available on this device. */
public static final int FEATURE_STATE_UNAVAILABLE = 0;
/** Feature state: Wifi display is disabled, probably because Wifi is disabled. */
@@ -76,8 +79,11 @@
displays[i] = WifiDisplay.CREATOR.createFromParcel(in);
}
+ WifiDisplaySessionInfo sessionInfo =
+ WifiDisplaySessionInfo.CREATOR.createFromParcel(in);
+
return new WifiDisplayStatus(featureState, scanState, activeDisplayState,
- activeDisplay, displays);
+ activeDisplay, displays, sessionInfo);
}
public WifiDisplayStatus[] newArray(int size) {
@@ -87,11 +93,11 @@
public WifiDisplayStatus() {
this(FEATURE_STATE_UNAVAILABLE, SCAN_STATE_NOT_SCANNING, DISPLAY_STATE_NOT_CONNECTED,
- null, WifiDisplay.EMPTY_ARRAY);
+ null, WifiDisplay.EMPTY_ARRAY, null);
}
- public WifiDisplayStatus(int featureState, int scanState,
- int activeDisplayState, WifiDisplay activeDisplay, WifiDisplay[] displays) {
+ public WifiDisplayStatus(int featureState, int scanState, int activeDisplayState,
+ WifiDisplay activeDisplay, WifiDisplay[] displays, WifiDisplaySessionInfo sessionInfo) {
if (displays == null) {
throw new IllegalArgumentException("displays must not be null");
}
@@ -101,6 +107,8 @@
mActiveDisplayState = activeDisplayState;
mActiveDisplay = activeDisplay;
mDisplays = displays;
+
+ mSessionInfo = (sessionInfo != null) ? sessionInfo : new WifiDisplaySessionInfo();
}
/**
@@ -144,13 +152,20 @@
/**
* Gets the list of Wifi displays, returns a combined list of all available
- * Wifi displays as reported by the most recent scan, and all remembered
+ * Wifi displays as reported by the most recent scan, and all remembered
* Wifi displays (not necessarily available at the time).
*/
public WifiDisplay[] getDisplays() {
return mDisplays;
}
+ /**
+ * Gets the Wifi display session info (required for certification only)
+ */
+ public WifiDisplaySessionInfo getSessionInfo() {
+ return mSessionInfo;
+ }
+
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mFeatureState);
@@ -168,6 +183,8 @@
for (WifiDisplay display : mDisplays) {
display.writeToParcel(dest, flags);
}
+
+ mSessionInfo.writeToParcel(dest, flags);
}
@Override
@@ -183,6 +200,7 @@
+ ", activeDisplayState=" + mActiveDisplayState
+ ", activeDisplay=" + mActiveDisplay
+ ", displays=" + Arrays.toString(mDisplays)
+ + ", sessionInfo=" + mSessionInfo
+ "}";
}
}
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index da7647a..65c9220 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -16,6 +16,9 @@
package android.provider;
+import static android.net.TrafficStats.KB_IN_BYTES;
+import static libcore.io.OsConstants.SEEK_SET;
+
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.ContentValues;
@@ -30,19 +33,49 @@
import android.graphics.Point;
import android.net.Uri;
import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.os.ParcelFileDescriptor.OnCloseListener;
import android.util.Log;
import com.google.android.collect.Lists;
+import libcore.io.ErrnoException;
+import libcore.io.IoBridge;
import libcore.io.IoUtils;
+import libcore.io.Libcore;
import java.io.FileDescriptor;
import java.io.IOException;
import java.util.List;
/**
- * The contract between a storage backend and the platform. Contains definitions
- * for the supported URIs and columns.
+ * Defines the contract between a documents provider and the platform.
+ * <p>
+ * A document provider is a {@link ContentProvider} that presents a set of
+ * documents in a hierarchical structure. The system provides UI that visualizes
+ * all available document providers, offering users the ability to open existing
+ * documents or create new documents.
+ * <p>
+ * Each provider expresses one or more "roots" which each serve as the top-level
+ * of a tree. For example, a root could represent an account, or a physical
+ * storage device. Under each root, documents are referenced by a unique
+ * {@link DocumentColumns#DOC_ID}, and each root starts at the
+ * {@link Documents#DOC_ID_ROOT} document.
+ * <p>
+ * Documents can be either an openable file (with a specific MIME type), or a
+ * directory containing additional documents (with the
+ * {@link Documents#MIME_TYPE_DIR} MIME type). Each document can have different
+ * capabilities, as described by {@link DocumentColumns#FLAGS}. The same
+ * {@link DocumentColumns#DOC_ID} can be included in multiple directories.
+ * <p>
+ * Document providers must be protected with the
+ * {@link android.Manifest.permission#MANAGE_DOCUMENTS} permission, which can
+ * only be requested by the system. The system-provided UI then issues narrow
+ * Uri permission grants for individual documents when the user explicitly picks
+ * documents.
+ *
+ * @see Intent#ACTION_OPEN_DOCUMENT
+ * @see Intent#ACTION_CREATE_DOCUMENT
*/
public final class DocumentsContract {
private static final String TAG = "Documents";
@@ -59,6 +92,9 @@
/** {@hide} */
public static final String ACTION_DOCUMENT_CHANGED = "android.provider.action.DOCUMENT_CHANGED";
+ /**
+ * Constants for individual documents.
+ */
public static class Documents {
private Documents() {
}
@@ -73,7 +109,7 @@
/**
* {@link DocumentColumns#DOC_ID} value representing the root directory of a
- * storage root.
+ * documents root.
*/
public static final String DOC_ID_ROOT = "0";
@@ -144,8 +180,8 @@
/**
* Extra boolean flag included in a directory {@link Cursor#getExtras()}
- * indicating that the backend can provide additional data if requested,
- * such as additional search results.
+ * indicating that the document provider can provide additional data if
+ * requested, such as additional search results.
*/
public static final String EXTRA_HAS_MORE = "has_more";
@@ -170,21 +206,24 @@
private static final String PARAM_LOCAL_ONLY = "localOnly";
/**
- * Build URI representing the roots in a storage backend.
+ * Build Uri representing the roots offered by a document provider.
*/
public static Uri buildRootsUri(String authority) {
return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
.authority(authority).appendPath(PATH_ROOTS).build();
}
+ /**
+ * Build Uri representing a specific root offered by a document provider.
+ */
public static Uri buildRootUri(String authority, String rootId) {
return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
.authority(authority).appendPath(PATH_ROOTS).appendPath(rootId).build();
}
/**
- * Build URI representing the given {@link DocumentColumns#DOC_ID} in a
- * storage root.
+ * Build Uri representing the given {@link DocumentColumns#DOC_ID} in a
+ * document provider.
*/
public static Uri buildDocumentUri(String authority, String rootId, String docId) {
return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(authority)
@@ -193,8 +232,8 @@
}
/**
- * Build URI representing the contents of the given directory in a storage
- * backend. The given document must be {@link Documents#MIME_TYPE_DIR}.
+ * Build Uri representing the contents of the given directory in a document
+ * provider. The given document must be {@link Documents#MIME_TYPE_DIR}.
*/
public static Uri buildContentsUri(String authority, String rootId, String docId) {
return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(authority)
@@ -203,8 +242,9 @@
}
/**
- * Build URI representing a search for matching documents under a directory
- * in a storage backend.
+ * Build Uri representing a search for matching documents under a specific
+ * directory in a document provider. The given document must have
+ * {@link Documents#FLAG_SUPPORTS_SEARCH}.
*/
public static Uri buildSearchUri(String authority, String rootId, String docId, String query) {
return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(authority)
@@ -212,20 +252,36 @@
.appendPath(PATH_SEARCH).appendQueryParameter(PARAM_QUERY, query).build();
}
+ /**
+ * Convenience method for {@link #buildDocumentUri(String, String, String)},
+ * extracting authority and root from the given Uri.
+ */
public static Uri buildDocumentUri(Uri relatedUri, String docId) {
return buildDocumentUri(relatedUri.getAuthority(), getRootId(relatedUri), docId);
}
+ /**
+ * Convenience method for {@link #buildContentsUri(String, String, String)},
+ * extracting authority and root from the given Uri.
+ */
public static Uri buildContentsUri(Uri relatedUri) {
return buildContentsUri(
relatedUri.getAuthority(), getRootId(relatedUri), getDocId(relatedUri));
}
+ /**
+ * Convenience method for
+ * {@link #buildSearchUri(String, String, String, String)}, extracting
+ * authority and root from the given Uri.
+ */
public static Uri buildSearchUri(Uri relatedUri, String query) {
return buildSearchUri(
relatedUri.getAuthority(), getRootId(relatedUri), getDocId(relatedUri), query);
}
+ /**
+ * Extract the {@link RootColumns#ROOT_ID} from the given Uri.
+ */
public static String getRootId(Uri documentUri) {
final List<String> paths = documentUri.getPathSegments();
if (paths.size() < 2) {
@@ -237,6 +293,9 @@
return paths.get(1);
}
+ /**
+ * Extract the {@link DocumentColumns#DOC_ID} from the given Uri.
+ */
public static String getDocId(Uri documentUri) {
final List<String> paths = documentUri.getPathSegments();
if (paths.size() < 4) {
@@ -252,15 +311,17 @@
}
/**
- * Return requested search query from the given Uri.
+ * Return requested search query from the given Uri, as constructed by
+ * {@link #buildSearchUri(String, String, String, String)}.
*/
public static String getSearchQuery(Uri documentUri) {
return documentUri.getQueryParameter(PARAM_QUERY);
}
/**
- * Mark the given Uri to indicate that only locally-available contents
- * should be returned.
+ * Mark the given Uri to indicate that only locally-available data should be
+ * returned. That is, no network connections should be initiated to provide
+ * the metadata or content.
*/
public static Uri setLocalOnly(Uri documentUri) {
return documentUri.buildUpon()
@@ -268,20 +329,21 @@
}
/**
- * Return if the given Uri is requesting that only locally-available content
- * be returned. That is, no network connections should be initiated to
- * provide the metadata or content.
+ * Return if the given Uri is requesting that only locally-available data be
+ * returned. That is, no network connections should be initiated to provide
+ * the metadata or content.
*/
public static boolean isLocalOnly(Uri documentUri) {
return documentUri.getBooleanQueryParameter(PARAM_LOCAL_ONLY, false);
}
/**
- * These are standard columns for document URIs. Storage backend providers
- * <em>must</em> support at least these columns when queried.
+ * Standard columns for document queries. Document providers <em>must</em>
+ * support at least these columns when queried.
*
- * @see Intent#ACTION_OPEN_DOCUMENT
- * @see Intent#ACTION_CREATE_DOCUMENT
+ * @see DocumentsContract#buildDocumentUri(String, String, String)
+ * @see DocumentsContract#buildContentsUri(String, String, String)
+ * @see DocumentsContract#buildSearchUri(String, String, String, String)
*/
public interface DocumentColumns extends OpenableColumns {
/**
@@ -296,8 +358,8 @@
/**
* MIME type of a document, matching the value returned by
* {@link ContentResolver#getType(android.net.Uri)}. This field must be
- * provided when a new document is created, but after that the field is
- * read-only.
+ * provided when a new document is created. This field is read-only to
+ * document clients.
* <p>
* Type: STRING
*
@@ -308,7 +370,9 @@
/**
* Timestamp when a document was last modified, in milliseconds since
* January 1, 1970 00:00:00.0 UTC. This field is read-only to document
- * clients.
+ * clients. Document providers can update this field using events from
+ * {@link OnCloseListener} or other reliable
+ * {@link ParcelFileDescriptor} transport.
* <p>
* Type: INTEGER (long)
*
@@ -325,13 +389,17 @@
public static final String FLAGS = "flags";
/**
- * Summary for this document, or {@code null} to omit.
+ * Summary for this document, or {@code null} to omit. This field is
+ * read-only to document clients.
* <p>
* Type: STRING
*/
public static final String SUMMARY = "summary";
}
+ /**
+ * Constants for individual document roots.
+ */
public static class Roots {
private Roots() {
}
@@ -340,7 +408,8 @@
public static final String MIME_TYPE_ITEM = "vnd.android.cursor.item/root";
/**
- * Root that represents a cloud-based storage service.
+ * Root that represents a storage service, such as a cloud-based
+ * service.
*
* @see RootColumns#ROOT_TYPE
*/
@@ -371,15 +440,17 @@
}
/**
- * These are standard columns for the roots URI.
+ * Standard columns for document root queries.
*
* @see DocumentsContract#buildRootsUri(String)
+ * @see DocumentsContract#buildRootUri(String, String)
*/
public interface RootColumns {
public static final String ROOT_ID = "root_id";
/**
- * Storage root type, use for clustering.
+ * Storage root type, use for clustering. This field is read-only to
+ * document clients.
* <p>
* Type: INTEGER (int)
*
@@ -389,8 +460,9 @@
public static final String ROOT_TYPE = "root_type";
/**
- * Icon resource ID for this storage root, or {@code 0} to use the
- * default {@link ProviderInfo#icon}.
+ * Icon resource ID for this storage root, or {@code null} to use the
+ * default {@link ProviderInfo#icon}. This field is read-only to
+ * document clients.
* <p>
* Type: INTEGER (int)
*/
@@ -398,22 +470,25 @@
/**
* Title for this storage root, or {@code null} to use the default
- * {@link ProviderInfo#labelRes}.
+ * {@link ProviderInfo#labelRes}. This field is read-only to document
+ * clients.
* <p>
* Type: STRING
*/
public static final String TITLE = "title";
/**
- * Summary for this storage root, or {@code null} to omit.
+ * Summary for this storage root, or {@code null} to omit. This field is
+ * read-only to document clients.
* <p>
* Type: STRING
*/
public static final String SUMMARY = "summary";
/**
- * Number of free bytes of available in this storage root, or -1 if
- * unknown or unbounded.
+ * Number of free bytes of available in this storage root, or
+ * {@code null} if unknown or unbounded. This field is read-only to
+ * document clients.
* <p>
* Type: INTEGER (long)
*/
@@ -452,31 +527,59 @@
/**
* Return thumbnail representing the document at the given URI. Callers are
- * responsible for their own caching. Given document must have
+ * responsible for their own in-memory caching. Given document must have
* {@link Documents#FLAG_SUPPORTS_THUMBNAIL} set.
*
* @return decoded thumbnail, or {@code null} if problem was encountered.
*/
public static Bitmap getThumbnail(ContentResolver resolver, Uri documentUri, Point size) {
- final Bundle opts = new Bundle();
- opts.putParcelable(EXTRA_THUMBNAIL_SIZE, size);
+ final Bundle openOpts = new Bundle();
+ openOpts.putParcelable(DocumentsContract.EXTRA_THUMBNAIL_SIZE, size);
AssetFileDescriptor afd = null;
try {
- afd = resolver.openTypedAssetFileDescriptor(documentUri, "image/*", opts);
+ afd = resolver.openTypedAssetFileDescriptor(documentUri, "image/*", openOpts);
final FileDescriptor fd = afd.getFileDescriptor();
- final BitmapFactory.Options bitmapOpts = new BitmapFactory.Options();
+ final long offset = afd.getStartOffset();
+ final long length = afd.getDeclaredLength();
- bitmapOpts.inJustDecodeBounds = true;
- BitmapFactory.decodeFileDescriptor(fd, null, bitmapOpts);
+ // Some thumbnails might be a region inside a larger file, such as
+ // an EXIF thumbnail. Since BitmapFactory aggressively seeks around
+ // the entire file, we read the region manually.
+ byte[] region = null;
+ if (offset > 0 && length <= 64 * KB_IN_BYTES) {
+ region = new byte[(int) length];
+ Libcore.os.lseek(fd, offset, SEEK_SET);
+ if (IoBridge.read(fd, region, 0, region.length) != region.length) {
+ region = null;
+ }
+ }
- final int widthSample = bitmapOpts.outWidth / size.x;
- final int heightSample = bitmapOpts.outHeight / size.y;
+ // We requested a rough thumbnail size, but the remote size may have
+ // returned something giant, so defensively scale down as needed.
+ final BitmapFactory.Options opts = new BitmapFactory.Options();
+ opts.inJustDecodeBounds = true;
+ if (region != null) {
+ BitmapFactory.decodeByteArray(region, 0, region.length, opts);
+ } else {
+ BitmapFactory.decodeFileDescriptor(fd, null, opts);
+ }
- bitmapOpts.inJustDecodeBounds = false;
- bitmapOpts.inSampleSize = Math.min(widthSample, heightSample);
- return BitmapFactory.decodeFileDescriptor(fd, null, bitmapOpts);
+ final int widthSample = opts.outWidth / size.x;
+ final int heightSample = opts.outHeight / size.y;
+
+ opts.inJustDecodeBounds = false;
+ opts.inSampleSize = Math.min(widthSample, heightSample);
+ Log.d(TAG, "Decoding with sample size " + opts.inSampleSize);
+ if (region != null) {
+ return BitmapFactory.decodeByteArray(region, 0, region.length, opts);
+ } else {
+ return BitmapFactory.decodeFileDescriptor(fd, null, opts);
+ }
+ } catch (ErrnoException e) {
+ Log.w(TAG, "Failed to load thumbnail for " + documentUri + ": " + e);
+ return null;
} catch (IOException e) {
Log.w(TAG, "Failed to load thumbnail for " + documentUri + ": " + e);
return null;
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index b3309e1a..0b51b8a 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5075,6 +5075,14 @@
public static final String WIFI_DISPLAY_ON = "wifi_display_on";
/**
+ * Whether Wifi display certification mode is enabled/disabled
+ * 0=disabled. 1=enabled.
+ * @hide
+ */
+ public static final String WIFI_DISPLAY_CERTIFICATION_ON =
+ "wifi_display_certification_on";
+
+ /**
* Whether to notify the user of open networks.
* <p>
* If not connected and the scan results have an open network, we will
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 521ba81..1f2ab93 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -138,6 +138,7 @@
= "lockscreen.biometricweakeverchosen";
public final static String LOCKSCREEN_POWER_BUTTON_INSTANTLY_LOCKS
= "lockscreen.power_button_instantly_locks";
+ public final static String LOCKSCREEN_WIDGETS_ENABLED = "lockscreen.widgets_enabled";
public final static String PASSWORD_HISTORY_KEY = "lockscreen.passwordhistory";
@@ -1053,28 +1054,38 @@
return nextAlarm;
}
- private boolean getBoolean(String secureSettingKey, boolean defaultValue) {
+ private boolean getBoolean(String secureSettingKey, boolean defaultValue, int userId) {
try {
- return getLockSettings().getBoolean(secureSettingKey, defaultValue,
- getCurrentOrCallingUserId());
+ return getLockSettings().getBoolean(secureSettingKey, defaultValue, userId);
} catch (RemoteException re) {
return defaultValue;
}
}
- private void setBoolean(String secureSettingKey, boolean enabled) {
+ private boolean getBoolean(String secureSettingKey, boolean defaultValue) {
+ return getBoolean(secureSettingKey, defaultValue, getCurrentOrCallingUserId());
+ }
+
+ private void setBoolean(String secureSettingKey, boolean enabled, int userId) {
try {
- getLockSettings().setBoolean(secureSettingKey, enabled, getCurrentOrCallingUserId());
+ getLockSettings().setBoolean(secureSettingKey, enabled, userId);
} catch (RemoteException re) {
// What can we do?
Log.e(TAG, "Couldn't write boolean " + secureSettingKey + re);
}
}
+ private void setBoolean(String secureSettingKey, boolean enabled) {
+ setBoolean(secureSettingKey, enabled, getCurrentOrCallingUserId());
+ }
+
public int[] getAppWidgets() {
+ return getAppWidgets(UserHandle.USER_CURRENT);
+ }
+
+ private int[] getAppWidgets(int userId) {
String appWidgetIdString = Settings.Secure.getStringForUser(
- mContentResolver, Settings.Secure.LOCK_SCREEN_APPWIDGET_IDS,
- UserHandle.USER_CURRENT);
+ mContentResolver, Settings.Secure.LOCK_SCREEN_APPWIDGET_IDS, userId);
String delims = ",";
if (appWidgetIdString != null && appWidgetIdString.length() > 0) {
String[] appWidgetStringIds = appWidgetIdString.split(delims);
@@ -1361,4 +1372,35 @@
return false;
}
+ /**
+ * Determine whether the user has selected any non-system widgets in keyguard
+ *
+ * @return true if widgets have been selected
+ */
+ public boolean hasWidgetsEnabledInKeyguard(int userid) {
+ int widgets[] = getAppWidgets(userid);
+ for (int i = 0; i < widgets.length; i++) {
+ if (widgets[i] > 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public boolean getWidgetsEnabled() {
+ return getWidgetsEnabled(getCurrentOrCallingUserId());
+ }
+
+ public boolean getWidgetsEnabled(int userId) {
+ return getBoolean(LOCKSCREEN_WIDGETS_ENABLED, false, userId);
+ }
+
+ public void setWidgetsEnabled(boolean enabled) {
+ setWidgetsEnabled(enabled, getCurrentOrCallingUserId());
+ }
+
+ public void setWidgetsEnabled(boolean enabled, int userId) {
+ setBoolean(LOCKSCREEN_WIDGETS_ENABLED, enabled, userId);
+ }
+
}
diff --git a/core/jni/android_media_RemoteDisplay.cpp b/core/jni/android_media_RemoteDisplay.cpp
index 80d13be..3fd8ed9 100644
--- a/core/jni/android_media_RemoteDisplay.cpp
+++ b/core/jni/android_media_RemoteDisplay.cpp
@@ -61,7 +61,7 @@
public:
virtual void onDisplayConnected(const sp<IGraphicBufferProducer>& bufferProducer,
- uint32_t width, uint32_t height, uint32_t flags) {
+ uint32_t width, uint32_t height, uint32_t flags, uint32_t session) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
jobject surfaceObj = android_view_Surface_createFromIGraphicBufferProducer(env, bufferProducer);
@@ -73,7 +73,7 @@
env->CallVoidMethod(mRemoteDisplayObjGlobal,
gRemoteDisplayClassInfo.notifyDisplayConnected,
- surfaceObj, width, height, flags);
+ surfaceObj, width, height, flags, session);
env->DeleteLocalRef(surfaceObj);
checkAndClearExceptionFromCallback(env, "notifyDisplayConnected");
}
@@ -117,6 +117,14 @@
mDisplay->dispose();
}
+ void pause() {
+ mDisplay->pause();
+ }
+
+ void resume() {
+ mDisplay->resume();
+ }
+
private:
sp<IRemoteDisplay> mDisplay;
sp<NativeRemoteDisplayClient> mClient;
@@ -149,6 +157,16 @@
return reinterpret_cast<jint>(wrapper);
}
+static void nativePause(JNIEnv* env, jobject remoteDisplayObj, jint ptr) {
+ NativeRemoteDisplay* wrapper = reinterpret_cast<NativeRemoteDisplay*>(ptr);
+ wrapper->pause();
+}
+
+static void nativeResume(JNIEnv* env, jobject remoteDisplayObj, jint ptr) {
+ NativeRemoteDisplay* wrapper = reinterpret_cast<NativeRemoteDisplay*>(ptr);
+ wrapper->resume();
+}
+
static void nativeDispose(JNIEnv* env, jobject remoteDisplayObj, jint ptr) {
NativeRemoteDisplay* wrapper = reinterpret_cast<NativeRemoteDisplay*>(ptr);
delete wrapper;
@@ -161,6 +179,10 @@
(void*)nativeListen },
{"nativeDispose", "(I)V",
(void*)nativeDispose },
+ {"nativePause", "(I)V",
+ (void*)nativePause },
+ {"nativeResume", "(I)V",
+ (void*)nativeResume },
};
int register_android_media_RemoteDisplay(JNIEnv* env)
@@ -171,7 +193,7 @@
jclass clazz = env->FindClass("android/media/RemoteDisplay");
gRemoteDisplayClassInfo.notifyDisplayConnected =
env->GetMethodID(clazz, "notifyDisplayConnected",
- "(Landroid/view/Surface;III)V");
+ "(Landroid/view/Surface;IIII)V");
gRemoteDisplayClassInfo.notifyDisplayDisconnected =
env->GetMethodID(clazz, "notifyDisplayDisconnected", "()V");
gRemoteDisplayClassInfo.notifyDisplayError =
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 0d1ace8..faf6e63 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1900,14 +1900,14 @@
android:description="@string/permdesc_bindAccessibilityService"
android:protectionLevel="signature" />
- <!-- Must be required by an {@link android.printservice.PrintService},
+ <!-- Must be required by a {@link android.printservice.PrintService},
to ensure that only the system can bind to it. -->
<permission android:name="android.permission.BIND_PRINT_SERVICE"
android:label="@string/permlab_bindPrintService"
android:description="@string/permdesc_bindPrintService"
android:protectionLevel="signature" />
- <!-- Must be required by an {@link android.nfc.cardemulation.HostApduService}
+ <!-- Must be required by a {@link android.nfc.cardemulation.HostApduService}
or {@link android.nfc.cardemulation.OffHostApduService} to ensure that only
the system can bind to it. -->
<permission android:name="android.permission.BIND_NFC_SERVICE"
@@ -1931,7 +1931,7 @@
android:description="@string/permdesc_bindTextService"
android:protectionLevel="signature" />
- <!-- Must be required by an {@link android.net.VpnService},
+ <!-- Must be required by a {@link android.net.VpnService},
to ensure that only the system can bind to it. -->
<permission android:name="android.permission.BIND_VPN_SERVICE"
android:label="@string/permlab_bindVpnService"
@@ -1953,7 +1953,7 @@
android:protectionLevel="signature" />
<!-- Required to add or remove another application as a device admin.
- <p/>Not for use by third-party apps. -->
+ <p/>Not for use by third-party applications. -->
<permission android:name="android.permission.MANAGE_DEVICE_ADMINS"
android:label="@string/permlab_manageDeviceAdmins"
android:description="@string/permdesc_manageDeviceAdmins"
diff --git a/core/res/res/drawable-xxhdpi/stat_sys_adb_am.png b/core/res/res/drawable-xxhdpi/stat_sys_adb_am.png
index 0820f07..a36fa36 100644
--- a/core/res/res/drawable-xxhdpi/stat_sys_adb_am.png
+++ b/core/res/res/drawable-xxhdpi/stat_sys_adb_am.png
Binary files differ
diff --git a/drm/java/android/drm/DrmManagerClient.java b/drm/java/android/drm/DrmManagerClient.java
index 10cdab0..e2606d6 100644
--- a/drm/java/android/drm/DrmManagerClient.java
+++ b/drm/java/android/drm/DrmManagerClient.java
@@ -63,6 +63,8 @@
private final CloseGuard mCloseGuard = CloseGuard.get();
+ private static final String EXTENDED_INFO_DATA = "extended_info_data";
+
static {
// Load the respective library
System.loadLibrary("drmframework_jni");
@@ -184,8 +186,22 @@
DrmManagerClient instance = (DrmManagerClient)((WeakReference)thisReference).get();
if (null != instance && null != instance.mInfoHandler) {
+ DrmInfoEvent event = new DrmInfoEvent(uniqueId, infoType, message);
Message m = instance.mInfoHandler.obtainMessage(
- InfoHandler.INFO_EVENT_TYPE, uniqueId, infoType, message);
+ InfoHandler.INFO_EVENT_TYPE, event);
+ instance.mInfoHandler.sendMessage(m);
+ }
+ }
+
+ private static void notify(
+ Object thisReference, int uniqueId, int infoType, String message,
+ HashMap<String, Object> attributes) {
+ DrmManagerClient instance = (DrmManagerClient)((WeakReference)thisReference).get();
+
+ if (null != instance && null != instance.mInfoHandler) {
+ DrmInfoEvent event = new DrmInfoEvent(uniqueId, infoType, message, attributes);
+ Message m = instance.mInfoHandler.obtainMessage(
+ InfoHandler.INFO_EVENT_TYPE, event);
instance.mInfoHandler.sendMessage(m);
}
}
@@ -198,23 +214,25 @@
}
public void handleMessage(Message msg) {
- DrmInfoEvent info = null;
+ DrmInfoEvent info = (DrmInfoEvent) msg.obj;
DrmErrorEvent error = null;
+ int uniqueId;
+ int eventType;
+ String message;
switch (msg.what) {
case InfoHandler.INFO_EVENT_TYPE:
- int uniqueId = msg.arg1;
- int infoType = msg.arg2;
- String message = msg.obj.toString();
+ uniqueId = info.getUniqueId();
+ eventType = info.getType();
+ message = info.getMessage();
- switch (infoType) {
+ switch (eventType) {
case DrmInfoEvent.TYPE_REMOVE_RIGHTS: {
try {
DrmUtils.removeFile(message);
} catch (IOException e) {
e.printStackTrace();
}
- info = new DrmInfoEvent(uniqueId, infoType, message);
break;
}
case DrmInfoEvent.TYPE_ALREADY_REGISTERED_BY_ANOTHER_ACCOUNT:
@@ -222,11 +240,11 @@
case DrmInfoEvent.TYPE_WAIT_FOR_RIGHTS:
case DrmInfoEvent.TYPE_ACCOUNT_ALREADY_REGISTERED:
case DrmInfoEvent.TYPE_RIGHTS_REMOVED: {
- info = new DrmInfoEvent(uniqueId, infoType, message);
break;
}
default:
- error = new DrmErrorEvent(uniqueId, infoType, message);
+ info = null;
+ error = new DrmErrorEvent(uniqueId, eventType, message);
break;
}
diff --git a/drm/jni/android_drm_DrmManagerClient.cpp b/drm/jni/android_drm_DrmManagerClient.cpp
index baddf62..7fce3d0 100644
--- a/drm/jni/android_drm_DrmManagerClient.cpp
+++ b/drm/jni/android_drm_DrmManagerClient.cpp
@@ -169,11 +169,49 @@
JNIEnv *env = AndroidRuntime::getJNIEnv();
jstring message = env->NewStringUTF(event.getMessage().string());
ALOGV("JNIOnInfoListener::onInfo => %d | %d | %s", uniqueId, type, event.getMessage().string());
+ const DrmBuffer& drmBuffer = event.getData();
+ if (event.getCount() > 0 || drmBuffer.length > 0) {
+ jclass hashMapClazz = env->FindClass("java/util/HashMap");
+ jmethodID hashMapInitId = env->GetMethodID(hashMapClazz, "<init>", "()V");
+ jmethodID hashMapPutId = env->GetMethodID(hashMapClazz, "put",
+ "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
+ jobject hashMapObject = env->NewObject(hashMapClazz, hashMapInitId);
+ env->DeleteLocalRef(hashMapClazz);
- env->CallStaticVoidMethod(
- mClass,
- env->GetStaticMethodID(mClass, "notify", "(Ljava/lang/Object;IILjava/lang/String;)V"),
- mObject, uniqueId, type, message);
+ if (0 < drmBuffer.length) {
+ jfieldID fid = env->GetStaticFieldID(
+ mClass, "EXTENDED_INFO_DATA", "Ljava/lang/String;");
+ jstring key = (jstring) env->GetStaticObjectField(mClass, fid);
+
+ jbyteArray valueByte = env->NewByteArray(drmBuffer.length);
+ env->SetByteArrayRegion(valueByte, 0, drmBuffer.length, (jbyte*) drmBuffer.data);
+ env->CallObjectMethod(hashMapObject, hashMapPutId, key, valueByte);
+ env->DeleteLocalRef(valueByte);
+ env->DeleteLocalRef(key);
+ }
+ DrmInfoEvent::KeyIterator keyIt = event.keyIterator();
+ while (keyIt.hasNext()) {
+ String8 mapKey = keyIt.next();
+ jstring key = env->NewStringUTF(mapKey.string());
+ jstring value = env->NewStringUTF(event.get(mapKey).string());
+ env->CallObjectMethod(hashMapObject, hashMapPutId, key, value);
+ env->DeleteLocalRef(value);
+ env->DeleteLocalRef(key);
+ }
+ env->CallStaticVoidMethod(
+ mClass,
+ env->GetStaticMethodID(mClass, "notify",
+ "(Ljava/lang/Object;IILjava/lang/String;Ljava/util/HashMap;)V"),
+ mObject, uniqueId, type, message, hashMapObject);
+ env->DeleteLocalRef(hashMapObject);
+ } else {
+ env->CallStaticVoidMethod(
+ mClass,
+ env->GetStaticMethodID(mClass, "notify",
+ "(Ljava/lang/Object;IILjava/lang/String;)V"),
+ mObject, uniqueId, type, message);
+ }
+ env->DeleteLocalRef(message);
}
static Mutex sLock;
diff --git a/graphics/java/android/renderscript/Allocation.java b/graphics/java/android/renderscript/Allocation.java
index 362b586..7d05a74 100644
--- a/graphics/java/android/renderscript/Allocation.java
+++ b/graphics/java/android/renderscript/Allocation.java
@@ -61,6 +61,7 @@
Bitmap mBitmap;
int mUsage;
Allocation mAdaptedAllocation;
+ int mSize;
boolean mConstrainedLOD;
boolean mConstrainedFace;
@@ -268,10 +269,22 @@
mType = t;
mUsage = usage;
+ mSize = mType.getCount() * mType.getElement().getBytesSize();
if (t != null) {
updateCacheInfo(t);
}
+ try {
+ RenderScript.registerNativeAllocation.invoke(RenderScript.sRuntime, mSize);
+ } catch (Exception e) {
+ Log.e(RenderScript.LOG_TAG, "Couldn't invoke registerNativeAllocation:" + e);
+ throw new RSRuntimeException("Couldn't invoke registerNativeAllocation:" + e);
+ }
+ }
+
+ protected void finalize() throws Throwable {
+ RenderScript.registerNativeFree.invoke(RenderScript.sRuntime, mSize);
+ super.finalize();
}
private void validateIsInt32() {
diff --git a/graphics/java/android/renderscript/RenderScript.java b/graphics/java/android/renderscript/RenderScript.java
index 4de4766..854f079 100644
--- a/graphics/java/android/renderscript/RenderScript.java
+++ b/graphics/java/android/renderscript/RenderScript.java
@@ -18,6 +18,7 @@
import java.io.File;
import java.lang.reflect.Field;
+import java.lang.reflect.Method;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -61,11 +62,24 @@
static boolean sInitialized;
native static void _nInit();
+ static Object sRuntime;
+ static Method registerNativeAllocation;
+ static Method registerNativeFree;
static {
sInitialized = false;
if (!SystemProperties.getBoolean("config.disable_renderscript", false)) {
try {
+ Class<?> vm_runtime = Class.forName("dalvik.system.VMRuntime");
+ Method get_runtime = vm_runtime.getDeclaredMethod("getRuntime");
+ sRuntime = get_runtime.invoke(null);
+ registerNativeAllocation = vm_runtime.getDeclaredMethod("registerNativeAllocation", Integer.TYPE);
+ registerNativeFree = vm_runtime.getDeclaredMethod("registerNativeFree", Integer.TYPE);
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Error loading GC methods: " + e);
+ throw new RSRuntimeException("Error loading GC methods: " + e);
+ }
+ try {
System.loadLibrary("rs_jni");
_nInit();
sInitialized = true;
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index 4cd3e37..20eb356 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -291,6 +291,20 @@
}
/**
+ * Returns the offset and length of thumbnail inside the JPEG file, or
+ * {@code null} if there is no thumbnail.
+ *
+ * @return two-element array, the offset in the first value, and length in
+ * the second, or {@code null} if no thumbnail was found.
+ * @hide
+ */
+ public long[] getThumbnailRange() {
+ synchronized (sLock) {
+ return getThumbnailRangeNative(mFilename);
+ }
+ }
+
+ /**
* Stores the latitude and longitude value in a float array. The first element is
* the latitude, and the second element is the longitude. Returns false if the
* Exif tags are not available.
@@ -416,4 +430,6 @@
private native void commitChangesNative(String fileName);
private native byte[] getThumbnailNative(String fileName);
+
+ private native long[] getThumbnailRangeNative(String fileName);
}
diff --git a/media/java/android/media/RemoteDisplay.java b/media/java/android/media/RemoteDisplay.java
index b463d26..7afce1a 100644
--- a/media/java/android/media/RemoteDisplay.java
+++ b/media/java/android/media/RemoteDisplay.java
@@ -42,6 +42,8 @@
private native int nativeListen(String iface);
private native void nativeDispose(int ptr);
+ private native void nativePause(int ptr);
+ private native void nativeResume(int ptr);
private RemoteDisplay(Listener listener, Handler handler) {
mListener = listener;
@@ -87,6 +89,14 @@
dispose(false);
}
+ public void pause() {
+ nativePause(mPtr);
+ }
+
+ public void resume() {
+ nativeResume(mPtr);
+ }
+
private void dispose(boolean finalized) {
if (mPtr != 0) {
if (mGuard != null) {
@@ -113,11 +123,11 @@
// Called from native.
private void notifyDisplayConnected(final Surface surface,
- final int width, final int height, final int flags) {
+ final int width, final int height, final int flags, final int session) {
mHandler.post(new Runnable() {
@Override
public void run() {
- mListener.onDisplayConnected(surface, width, height, flags);
+ mListener.onDisplayConnected(surface, width, height, flags, session);
}
});
}
@@ -146,7 +156,8 @@
* Listener invoked when the remote display connection changes state.
*/
public interface Listener {
- void onDisplayConnected(Surface surface, int width, int height, int flags);
+ void onDisplayConnected(Surface surface,
+ int width, int height, int flags, int session);
void onDisplayDisconnected();
void onDisplayError(int error);
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index 091737d..22e3b98 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -584,7 +584,6 @@
SaveFragment.get(fm).setReplaceTarget(doc);
} else if (mAction == ACTION_MANAGE) {
// Open the document
- // TODO: trampoline activity for launching downloaded APKs
final Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setData(doc.uri);
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index b4bf563..8843e19 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -20,10 +20,13 @@
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.UriMatcher;
+import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.database.MatrixCursor.RowBuilder;
+import android.media.ExifInterface;
import android.net.Uri;
+import android.os.Bundle;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.provider.DocumentsContract;
@@ -296,7 +299,6 @@
final Root root = mRoots.get(DocumentsContract.getRootId(uri));
final String docId = DocumentsContract.getDocId(uri);
- // TODO: offer as thumbnail
final File file = docIdToFile(root, docId);
return ParcelFileDescriptor.open(file, ContentResolver.modeToMode(uri, mode));
}
@@ -307,6 +309,39 @@
}
@Override
+ public AssetFileDescriptor openTypedAssetFile(Uri uri, String mimeTypeFilter, Bundle opts)
+ throws FileNotFoundException {
+ if (opts == null || !opts.containsKey(DocumentsContract.EXTRA_THUMBNAIL_SIZE)) {
+ return super.openTypedAssetFile(uri, mimeTypeFilter, opts);
+ }
+
+ switch (sMatcher.match(uri)) {
+ case URI_DOCS_ID: {
+ final Root root = mRoots.get(DocumentsContract.getRootId(uri));
+ final String docId = DocumentsContract.getDocId(uri);
+
+ final File file = docIdToFile(root, docId);
+ final ParcelFileDescriptor pfd = ParcelFileDescriptor.open(
+ file, ParcelFileDescriptor.MODE_READ_ONLY);
+
+ try {
+ final ExifInterface exif = new ExifInterface(file.getAbsolutePath());
+ final long[] thumb = exif.getThumbnailRange();
+ if (thumb != null) {
+ return new AssetFileDescriptor(pfd, thumb[0], thumb[1]);
+ }
+ } catch (IOException e) {
+ }
+
+ return new AssetFileDescriptor(pfd, 0, AssetFileDescriptor.UNKNOWN_LENGTH);
+ }
+ default: {
+ throw new UnsupportedOperationException("Unsupported Uri " + uri);
+ }
+ }
+ }
+
+ @Override
public Uri insert(Uri uri, ContentValues values) {
switch (sMatcher.match(uri)) {
case URI_DOCS_ID: {
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
index 2904f4c..d4b79b7 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
@@ -217,7 +217,7 @@
mCleanupAppWidgetsOnBootCompleted = true;
return;
}
- if (!mSafeModeEnabled && !widgetsDisabledByDpm()) {
+ if (!mSafeModeEnabled && !widgetsDisabled()) {
// Clean up appWidgetIds that are bound to lockscreen, but not actually used
// This is only to clean up after another bug: we used to not call
// deleteAppWidgetId when a user manually deleted a widget in keyguard. This code
@@ -413,8 +413,11 @@
return disabledFeatures;
}
- private boolean widgetsDisabledByDpm() {
- return (mDisabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL) != 0;
+ private boolean widgetsDisabled() {
+ boolean disabledByDpm =
+ (mDisabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL) != 0;
+ boolean disabledByUser = !mLockPatternUtils.getWidgetsEnabled();
+ return disabledByDpm || disabledByUser;
}
private boolean cameraDisabledByDpm() {
@@ -1149,7 +1152,7 @@
}
private void addDefaultWidgets() {
- if (!mSafeModeEnabled && !widgetsDisabledByDpm()) {
+ if (!mSafeModeEnabled && !widgetsDisabled()) {
LayoutInflater inflater = LayoutInflater.from(mContext);
View addWidget = inflater.inflate(R.layout.keyguard_add_widget, this, false);
mAppWidgetContainer.addWidget(addWidget, 0);
@@ -1209,7 +1212,7 @@
}
private void addWidgetsFromSettings() {
- if (mSafeModeEnabled || widgetsDisabledByDpm()) {
+ if (mSafeModeEnabled || widgetsDisabled()) {
return;
}
@@ -1246,7 +1249,6 @@
try {
mAppWidgetManager.bindAppWidgetId(appWidgetId, defaultAppWidget);
-
} catch (IllegalArgumentException e) {
Log.e(TAG, "Error when trying to bind default AppWidget: " + e);
mAppWidgetHost.deleteAppWidgetId(appWidgetId);
@@ -1254,6 +1256,7 @@
}
return appWidgetId;
}
+
public void checkAppWidgetConsistency() {
// Since this method may bind a widget (which we can't do until boot completed) we
// may have to defer it until after boot complete.
@@ -1272,7 +1275,8 @@
if (!widgetPageExists) {
final int insertPageIndex = getInsertPageIndex();
- final boolean userAddedWidgetsEnabled = !widgetsDisabledByDpm();
+ final boolean userAddedWidgetsEnabled = !widgetsDisabled();
+
boolean addedDefaultAppWidget = false;
if (!mSafeModeEnabled) {
diff --git a/services/java/com/android/server/LockSettingsService.java b/services/java/com/android/server/LockSettingsService.java
index e28a258..c5555c8 100644
--- a/services/java/com/android/server/LockSettingsService.java
+++ b/services/java/com/android/server/LockSettingsService.java
@@ -16,6 +16,7 @@
package com.android.server;
+import android.app.ActivityManagerNative;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
@@ -27,6 +28,9 @@
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
+import android.database.sqlite.SQLiteStatement;
+import android.media.AudioManager;
+import android.media.AudioService;
import android.os.Binder;
import android.os.Environment;
import android.os.RemoteException;
@@ -37,6 +41,7 @@
import android.provider.Settings.Secure;
import android.provider.Settings.SettingNotFoundException;
import android.text.TextUtils;
+import android.util.Log;
import android.util.Slog;
import com.android.internal.widget.ILockSettings;
@@ -391,7 +396,7 @@
private static final String TAG = "LockSettingsDB";
private static final String DATABASE_NAME = "locksettings.db";
- private static final int DATABASE_VERSION = 1;
+ private static final int DATABASE_VERSION = 2;
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
@@ -424,7 +429,45 @@
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int currentVersion) {
- // Nothing yet
+ int upgradeVersion = oldVersion;
+ if (upgradeVersion == 1) {
+ // Set the initial value for {@link LockPatternUtils#LOCKSCREEN_WIDGETS_ENABLED}
+ // during upgrade based on whether each user previously had widgets in keyguard.
+ maybeEnableWidgetSettingForUsers(db);
+ upgradeVersion = 2;
+ }
+
+ if (upgradeVersion != DATABASE_VERSION) {
+ Log.w(TAG, "Failed to upgrade database!");
+ }
+ }
+
+ private void maybeEnableWidgetSettingForUsers(SQLiteDatabase db) {
+ final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE);
+ final ContentResolver cr = mContext.getContentResolver();
+ final LockPatternUtils utils = new LockPatternUtils(mContext);
+ final List<UserInfo> users = um.getUsers();
+ for (int i = 0; i < users.size(); i++) {
+ final int userId = users.get(i).id;
+ final boolean enabled = utils.hasWidgetsEnabledInKeyguard(userId);
+ Log.v(TAG, "Widget upgrade uid=" + userId + ", enabled="
+ + enabled + ", w[]=" + utils.getAppWidgets());
+ loadSetting(db, LockPatternUtils.LOCKSCREEN_WIDGETS_ENABLED, userId, enabled);
+ }
+ }
+
+ private void loadSetting(SQLiteDatabase db, String key, int userId, boolean value) {
+ SQLiteStatement stmt = null;
+ try {
+ stmt = db.compileStatement(
+ "INSERT OR REPLACE INTO locksettings(name,user,value) VALUES(?,?,?);");
+ stmt.bindString(1, key);
+ stmt.bindLong(2, userId);
+ stmt.bindLong(3, value ? 1 : 0);
+ stmt.execute();
+ } finally {
+ if (stmt != null) stmt.close();
+ }
}
}
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 8f4b6c2..4678b85 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -7644,7 +7644,7 @@
sCallerIdentity.set(new Identity(
Binder.getCallingPid(), Binder.getCallingUid()));
try {
- pfd = cph.provider.openFile(null, uri, "r");
+ pfd = cph.provider.openFile(null, uri, "r", null);
} catch (FileNotFoundException e) {
// do nothing; pfd will be returned null
} finally {
diff --git a/services/java/com/android/server/display/DisplayManagerService.java b/services/java/com/android/server/display/DisplayManagerService.java
index 659163c..249c8b0 100644
--- a/services/java/com/android/server/display/DisplayManagerService.java
+++ b/services/java/com/android/server/display/DisplayManagerService.java
@@ -497,6 +497,48 @@
}
}
+ @Override
+ public void pauseWifiDisplay() {
+ if (mContext.checkCallingPermission(
+ android.Manifest.permission.CONFIGURE_WIFI_DISPLAY)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Requires CONFIGURE_WIFI_DISPLAY"
+ + "permission to pause a wifi display session.");
+ }
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mSyncRoot) {
+ if (mWifiDisplayAdapter != null) {
+ mWifiDisplayAdapter.requestPauseLocked();
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void resumeWifiDisplay() {
+ if (mContext.checkCallingPermission(
+ android.Manifest.permission.CONFIGURE_WIFI_DISPLAY)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Requires CONFIGURE_WIFI_DISPLAY"
+ + "permission to resume a wifi display session.");
+ }
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mSyncRoot) {
+ if (mWifiDisplayAdapter != null) {
+ mWifiDisplayAdapter.requestResumeLocked();
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
@Override // Binder call
public void disconnectWifiDisplay() {
final long token = Binder.clearCallingIdentity();
diff --git a/services/java/com/android/server/display/WifiDisplayAdapter.java b/services/java/com/android/server/display/WifiDisplayAdapter.java
index a9da30f..f7bbdf8 100644
--- a/services/java/com/android/server/display/WifiDisplayAdapter.java
+++ b/services/java/com/android/server/display/WifiDisplayAdapter.java
@@ -30,6 +30,7 @@
import android.content.res.Resources;
import android.hardware.display.DisplayManager;
import android.hardware.display.WifiDisplay;
+import android.hardware.display.WifiDisplaySessionInfo;
import android.hardware.display.WifiDisplayStatus;
import android.media.RemoteDisplay;
import android.os.Handler;
@@ -93,6 +94,7 @@
private WifiDisplay[] mDisplays = WifiDisplay.EMPTY_ARRAY;
private WifiDisplay[] mAvailableDisplays = WifiDisplay.EMPTY_ARRAY;
private WifiDisplay[] mRememberedDisplays = WifiDisplay.EMPTY_ARRAY;
+ private WifiDisplaySessionInfo mSessionInfo;
private boolean mPendingStatusChangeBroadcast;
private boolean mPendingNotificationUpdate;
@@ -204,6 +206,36 @@
return false;
}
+ public void requestPauseLocked() {
+ if (DEBUG) {
+ Slog.d(TAG, "requestPauseLocked");
+ }
+
+ getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ if (mDisplayController != null) {
+ mDisplayController.requestPause();
+ }
+ }
+ });
+ }
+
+ public void requestResumeLocked() {
+ if (DEBUG) {
+ Slog.d(TAG, "requestResumeLocked");
+ }
+
+ getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ if (mDisplayController != null) {
+ mDisplayController.requestResume();
+ }
+ }
+ });
+ }
+
public void requestDisconnectLocked() {
if (DEBUG) {
Slog.d(TAG, "requestDisconnectedLocked");
@@ -267,7 +299,7 @@
if (mCurrentStatus == null) {
mCurrentStatus = new WifiDisplayStatus(
mFeatureState, mScanState, mActiveDisplayState,
- mActiveDisplay, mDisplays);
+ mActiveDisplay, mDisplays, mSessionInfo);
}
if (DEBUG) {
@@ -580,6 +612,14 @@
}
@Override
+ public void onDisplaySessionInfo(WifiDisplaySessionInfo sessionInfo) {
+ synchronized (getSyncRoot()) {
+ mSessionInfo = sessionInfo;
+ scheduleStatusChangedBroadcastLocked();
+ }
+ }
+
+ @Override
public void onDisplayChanged(WifiDisplay display) {
synchronized (getSyncRoot()) {
display = mPersistentDataStore.applyWifiDisplayAlias(display);
diff --git a/services/java/com/android/server/display/WifiDisplayController.java b/services/java/com/android/server/display/WifiDisplayController.java
index 846a74d..cd201f5 100644
--- a/services/java/com/android/server/display/WifiDisplayController.java
+++ b/services/java/com/android/server/display/WifiDisplayController.java
@@ -25,6 +25,7 @@
import android.content.IntentFilter;
import android.database.ContentObserver;
import android.hardware.display.WifiDisplay;
+import android.hardware.display.WifiDisplaySessionInfo;
import android.hardware.display.WifiDisplayStatus;
import android.media.AudioManager;
import android.media.RemoteDisplay;
@@ -76,6 +77,7 @@
private static final int MAX_THROUGHPUT = 50;
private static final int CONNECTION_TIMEOUT_SECONDS = 60;
private static final int RTSP_TIMEOUT_SECONDS = 15;
+ private static final int RTSP_TIMEOUT_SECONDS_CERT_MODE = 120;
private static final int DISCOVER_PEERS_MAX_RETRIES = 10;
private static final int DISCOVER_PEERS_RETRY_DELAY_MILLIS = 500;
@@ -146,6 +148,10 @@
private int mAdvertisedDisplayHeight;
private int mAdvertisedDisplayFlags;
+ // Certification
+ private boolean mWifiDisplayCertMode;
+ private WifiP2pDevice mThisDevice;
+
public WifiDisplayController(Context context, Handler handler, Listener listener) {
mContext = context;
mHandler = handler;
@@ -158,6 +164,7 @@
intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
intentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
+ intentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
context.registerReceiver(mWifiP2pReceiver, intentFilter, null, mHandler);
ContentObserver settingsObserver = new ContentObserver(mHandler) {
@@ -170,6 +177,8 @@
final ContentResolver resolver = mContext.getContentResolver();
resolver.registerContentObserver(Settings.Global.getUriFor(
Settings.Global.WIFI_DISPLAY_ON), false, settingsObserver);
+ resolver.registerContentObserver(Settings.Global.getUriFor(
+ Settings.Global.WIFI_DISPLAY_CERTIFICATION_ON), false, settingsObserver);
updateSettings();
}
@@ -177,6 +186,8 @@
final ContentResolver resolver = mContext.getContentResolver();
mWifiDisplayOnSetting = Settings.Global.getInt(resolver,
Settings.Global.WIFI_DISPLAY_ON, 0) != 0;
+ mWifiDisplayCertMode = Settings.Global.getInt(resolver,
+ Settings.Global.WIFI_DISPLAY_CERTIFICATION_ON, 0) != 0;
updateWfdEnableState();
}
@@ -223,6 +234,18 @@
}
}
+ public void requestPause() {
+ if (mRemoteDisplay != null) {
+ mRemoteDisplay.pause();
+ }
+ }
+
+ public void requestResume() {
+ if (mRemoteDisplay != null) {
+ mRemoteDisplay.resume();
+ }
+ }
+
public void requestDisconnect() {
disconnect();
}
@@ -482,6 +505,7 @@
Slog.i(TAG, "Disconnecting from Wifi display: " + mConnectedDevice.deviceName);
mDisconnectingDevice = mConnectedDevice;
mConnectedDevice = null;
+ mConnectedDeviceGroupInfo = null;
unadvertiseDisplay();
@@ -548,8 +572,12 @@
return; // wait for asynchronous callback
}
- // Step 4. If we wanted to disconnect, then mission accomplished.
+ // Step 4. If we wanted to disconnect, or we're updating after starting an
+ // autonomous GO, then mission accomplished.
if (mDesiredDevice == null) {
+ if (mWifiDisplayCertMode) {
+ mListener.onDisplaySessionInfo(getSessionInfo(mConnectedDeviceGroupInfo, 0));
+ }
unadvertiseDisplay();
return; // done
}
@@ -625,13 +653,18 @@
mRemoteDisplay = RemoteDisplay.listen(iface, new RemoteDisplay.Listener() {
@Override
public void onDisplayConnected(Surface surface,
- int width, int height, int flags) {
+ int width, int height, int flags, int session) {
if (mConnectedDevice == oldDevice && !mRemoteDisplayConnected) {
Slog.i(TAG, "Opened RTSP connection with Wifi display: "
+ mConnectedDevice.deviceName);
mRemoteDisplayConnected = true;
mHandler.removeCallbacks(mRtspTimeout);
+ if (mWifiDisplayCertMode) {
+ mListener.onDisplaySessionInfo(
+ getSessionInfo(mConnectedDeviceGroupInfo, session));
+ }
+
final WifiDisplay display = createWifiDisplay(mConnectedDevice);
advertiseDisplay(display, surface, width, height, flags);
}
@@ -658,10 +691,31 @@
}
}, mHandler);
- mHandler.postDelayed(mRtspTimeout, RTSP_TIMEOUT_SECONDS * 1000);
+ // Use extended timeout value for certification, as some tests require user inputs
+ int rtspTimeout = mWifiDisplayCertMode ?
+ RTSP_TIMEOUT_SECONDS_CERT_MODE : RTSP_TIMEOUT_SECONDS;
+
+ mHandler.postDelayed(mRtspTimeout, rtspTimeout * 1000);
}
}
+ private WifiDisplaySessionInfo getSessionInfo(WifiP2pGroup info, int session) {
+ if (info == null) {
+ return null;
+ }
+ Inet4Address addr = getInterfaceAddress(info);
+ WifiDisplaySessionInfo sessionInfo = new WifiDisplaySessionInfo(
+ !info.getOwner().deviceAddress.equals(mThisDevice.deviceAddress),
+ session,
+ info.getOwner().deviceAddress + " " + info.getNetworkName(),
+ info.getPassphrase(),
+ (addr != null) ? addr.getHostAddress() : "");
+ if (DEBUG) {
+ Slog.d(TAG, sessionInfo.toString());
+ }
+ return sessionInfo;
+ }
+
private void handleStateChanged(boolean enabled) {
mWifiP2pEnabled = enabled;
updateWfdEnableState();
@@ -676,7 +730,7 @@
private void handleConnectionChanged(NetworkInfo networkInfo) {
mNetworkInfo = networkInfo;
if (mWfdEnabled && networkInfo.isConnected()) {
- if (mDesiredDevice != null) {
+ if (mDesiredDevice != null || mWifiDisplayCertMode) {
mWifiP2pManager.requestGroupInfo(mWifiP2pChannel, new GroupInfoListener() {
@Override
public void onGroupInfoAvailable(WifiP2pGroup info) {
@@ -698,6 +752,25 @@
return;
}
+ if (mWifiDisplayCertMode) {
+ boolean owner = info.getOwner().deviceAddress
+ .equals(mThisDevice.deviceAddress);
+ if (owner && info.getClientList().isEmpty()) {
+ // this is the case when we started Autonomous GO,
+ // and no client has connected, save group info
+ // and updateConnection()
+ mConnectingDevice = mDesiredDevice = null;
+ mConnectedDeviceGroupInfo = info;
+ updateConnection();
+ } else if (mConnectingDevice == null && mDesiredDevice == null) {
+ // this is the case when we received an incoming connection
+ // from the sink, update both mConnectingDevice and mDesiredDevice
+ // then proceed to updateConnection() below
+ mConnectingDevice = mDesiredDevice = owner ?
+ info.getClientList().iterator().next() : info.getOwner();
+ }
+ }
+
if (mConnectingDevice != null && mConnectingDevice == mDesiredDevice) {
Slog.i(TAG, "Connected to Wifi display: "
+ mConnectingDevice.deviceName);
@@ -712,6 +785,7 @@
});
}
} else {
+ mConnectedDeviceGroupInfo = null;
disconnect();
// After disconnection for a group, for some reason we have a tendency
@@ -910,6 +984,13 @@
}
handleConnectionChanged(networkInfo);
+ } else if (action.equals(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION)) {
+ mThisDevice = (WifiP2pDevice) intent.getParcelableExtra(
+ WifiP2pManager.EXTRA_WIFI_P2P_DEVICE);
+ if (DEBUG) {
+ Slog.d(TAG, "Received WIFI_P2P_THIS_DEVICE_CHANGED_ACTION: mThisDevice= "
+ + mThisDevice);
+ }
}
}
};
@@ -928,6 +1009,7 @@
void onDisplayChanged(WifiDisplay display);
void onDisplayConnected(WifiDisplay display,
Surface surface, int width, int height, int flags);
+ void onDisplaySessionInfo(WifiDisplaySessionInfo sessionInfo);
void onDisplayDisconnected();
}
}
diff --git a/services/jni/com_android_server_location_FlpHardwareProvider.cpp b/services/jni/com_android_server_location_FlpHardwareProvider.cpp
index 9465a68..b403ee6 100644
--- a/services/jni/com_android_server_location_FlpHardwareProvider.cpp
+++ b/services/jni/com_android_server_location_FlpHardwareProvider.cpp
@@ -217,6 +217,8 @@
}
// TODO: wire sources_used if Location class exposes them
+
+ env->DeleteLocalRef(locationClass);
}
/*
@@ -257,6 +259,8 @@
jmethodID getFlags = env->GetMethodID(batchOptionsClass, "getFlags", "()I");
batchOptions.flags = env->CallIntMethod(batchOptionsObject, getFlags);
+
+ env->DeleteLocalRef(batchOptionsClass);
}
/*
@@ -326,6 +330,8 @@
options->last_transition = env->CallIntMethod(geofenceRequestObject, getLastTransition);
// TODO: set data.sources_to_use when available
+
+ env->DeleteLocalRef(geofenceRequestClass);
}
/*
@@ -408,6 +414,8 @@
}
// TODO: wire FlpLocation::sources_used when needed
+
+ sCallbackEnv->DeleteLocalRef(locationClass);
}
/*
@@ -430,6 +438,8 @@
sCallbackEnv->SetObjectArrayElement(locationsArray, i, locationObject);
sCallbackEnv->DeleteLocalRef(locationObject);
}
+
+ sCallbackEnv->DeleteLocalRef(locationClass);
}
static void LocationCallback(int32_t locationsCount, FlpLocation** locations) {
@@ -455,6 +465,10 @@
locationsArray
);
CheckExceptions(sCallbackEnv, __FUNCTION__);
+
+ if(locationsArray != NULL) {
+ sCallbackEnv->DeleteLocalRef(locationsArray);
+ }
}
static void AcquireWakelock() {
@@ -522,6 +536,10 @@
sourcesUsed
);
CheckExceptions(sCallbackEnv, __FUNCTION__);
+
+ if(locationObject != NULL) {
+ sCallbackEnv->DeleteLocalRef(locationObject);
+ }
}
static void GeofenceMonitorStatusCallback(
@@ -545,6 +563,10 @@
locationObject
);
CheckExceptions(sCallbackEnv, __FUNCTION__);
+
+ if(locationObject != NULL) {
+ sCallbackEnv->DeleteLocalRef(locationObject);
+ }
}
static void GeofenceAddCallback(int32_t geofenceId, int32_t result) {
@@ -843,6 +865,7 @@
jobject geofenceObject = env->GetObjectArrayElement(geofenceRequestsArray, i);
TranslateGeofenceFromGeofenceHardwareRequestParcelable(env, geofenceObject, geofences[i]);
+ env->DeleteLocalRef(geofenceObject);
}
sFlpGeofencingInterface->add_geofences(geofenceRequestsCount, &geofences);
diff --git a/test-runner/src/android/test/mock/MockContentProvider.java b/test-runner/src/android/test/mock/MockContentProvider.java
index 373f24a..596ea0a 100644
--- a/test-runner/src/android/test/mock/MockContentProvider.java
+++ b/test-runner/src/android/test/mock/MockContentProvider.java
@@ -83,13 +83,15 @@
}
@Override
- public AssetFileDescriptor openAssetFile(String callingPackage, Uri url, String mode)
+ public AssetFileDescriptor openAssetFile(
+ String callingPackage, Uri url, String mode, ICancellationSignal signal)
throws RemoteException, FileNotFoundException {
return MockContentProvider.this.openAssetFile(url, mode);
}
@Override
- public ParcelFileDescriptor openFile(String callingPackage, Uri url, String mode)
+ public ParcelFileDescriptor openFile(
+ String callingPackage, Uri url, String mode, ICancellationSignal signal)
throws RemoteException, FileNotFoundException {
return MockContentProvider.this.openFile(url, mode);
}
@@ -126,7 +128,7 @@
@Override
public AssetFileDescriptor openTypedAssetFile(String callingPackage, Uri url,
- String mimeType, Bundle opts)
+ String mimeType, Bundle opts, ICancellationSignal signal)
throws RemoteException, FileNotFoundException {
return MockContentProvider.this.openTypedAssetFile(url, mimeType, opts);
}
diff --git a/test-runner/src/android/test/mock/MockIContentProvider.java b/test-runner/src/android/test/mock/MockIContentProvider.java
index 2b4fce6..b14ce49 100644
--- a/test-runner/src/android/test/mock/MockIContentProvider.java
+++ b/test-runner/src/android/test/mock/MockIContentProvider.java
@@ -61,11 +61,13 @@
throw new UnsupportedOperationException("unimplemented mock method");
}
- public ParcelFileDescriptor openFile(String callingPackage, Uri url, String mode) {
+ public ParcelFileDescriptor openFile(
+ String callingPackage, Uri url, String mode, ICancellationSignal signal) {
throw new UnsupportedOperationException("unimplemented mock method");
}
- public AssetFileDescriptor openAssetFile(String callingPackage, Uri uri, String mode) {
+ public AssetFileDescriptor openAssetFile(
+ String callingPackage, Uri uri, String mode, ICancellationSignal signal) {
throw new UnsupportedOperationException("unimplemented mock method");
}
@@ -104,7 +106,7 @@
}
public AssetFileDescriptor openTypedAssetFile(String callingPackage, Uri url, String mimeType,
- Bundle opts) throws RemoteException, FileNotFoundException {
+ Bundle opts, ICancellationSignal signal) throws RemoteException, FileNotFoundException {
throw new UnsupportedOperationException("unimplemented mock method");
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java
index 4aea38f..688cc87 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java
@@ -81,14 +81,16 @@
}
@Override
- public AssetFileDescriptor openAssetFile(String callingPackage, Uri arg0, String arg1)
+ public AssetFileDescriptor openAssetFile(
+ String callingPackage, Uri arg0, String arg1, ICancellationSignal signal)
throws RemoteException, FileNotFoundException {
// TODO Auto-generated method stub
return null;
}
@Override
- public ParcelFileDescriptor openFile(String callingPackage, Uri arg0, String arg1)
+ public ParcelFileDescriptor openFile(
+ String callingPackage, Uri arg0, String arg1, ICancellationSignal signal)
throws RemoteException, FileNotFoundException {
// TODO Auto-generated method stub
return null;
@@ -122,7 +124,7 @@
@Override
public AssetFileDescriptor openTypedAssetFile(String callingPackage, Uri arg0, String arg1,
- Bundle arg2) throws RemoteException, FileNotFoundException {
+ Bundle arg2, ICancellationSignal signal) throws RemoteException, FileNotFoundException {
// TODO Auto-generated method stub
return null;
}
diff --git a/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java
index 79c1163..d3342dd 100644
--- a/wifi/java/android/net/wifi/WifiNative.java
+++ b/wifi/java/android/net/wifi/WifiNative.java
@@ -619,6 +619,37 @@
return doBooleanCommand("P2P_LISTEN " + timeout);
}
+ public boolean p2pExtListen(boolean enable, int period, int interval) {
+ if (enable && interval < period) {
+ return false;
+ }
+ return doBooleanCommand("P2P_EXT_LISTEN"
+ + (enable ? (" " + period + " " + interval) : ""));
+ }
+
+ public boolean p2pSetChannel(int lc, int oc) {
+ if (DBG) Log.d(mTAG, "p2pSetChannel: lc="+lc+", oc="+oc);
+
+ if (lc >=1 && lc <= 11) {
+ if (!doBooleanCommand("P2P_SET listen_channel " + lc)) {
+ return false;
+ }
+ } else if (lc != 0) {
+ return false;
+ }
+
+ if (oc >= 1 && oc <= 165 ) {
+ int freq = (oc <= 14 ? 2407 : 5000) + oc * 5;
+ return doBooleanCommand("P2P_SET disallow_freq 1000-"
+ + (freq - 5) + "," + (freq + 5) + "-6000");
+ } else if (oc == 0) {
+ /* oc==0 disables "P2P_SET disallow_freq" (enables all freqs) */
+ return doBooleanCommand("P2P_SET disallow_freq \"\"");
+ }
+
+ return false;
+ }
+
public boolean p2pFlush() {
return doBooleanCommand("P2P_FLUSH");
}
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
index 737ab91..4988b92 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
@@ -30,6 +30,7 @@
import android.net.wifi.p2p.nsd.WifiP2pUpnpServiceInfo;
import android.net.wifi.p2p.nsd.WifiP2pUpnpServiceResponse;
import android.os.Binder;
+import android.os.Bundle;
import android.os.IBinder;
import android.os.Handler;
import android.os.Looper;
@@ -430,6 +431,28 @@
/** @hide */
public static final int START_WPS_SUCCEEDED = BASE + 64;
+ /** @hide */
+ public static final int START_LISTEN = BASE + 65;
+ /** @hide */
+ public static final int START_LISTEN_FAILED = BASE + 66;
+ /** @hide */
+ public static final int START_LISTEN_SUCCEEDED = BASE + 67;
+
+ /** @hide */
+ public static final int STOP_LISTEN = BASE + 68;
+ /** @hide */
+ public static final int STOP_LISTEN_FAILED = BASE + 69;
+ /** @hide */
+ public static final int STOP_LISTEN_SUCCEEDED = BASE + 70;
+
+ /** @hide */
+ public static final int SET_CHANNEL = BASE + 71;
+ /** @hide */
+ public static final int SET_CHANNEL_FAILED = BASE + 72;
+ /** @hide */
+ public static final int SET_CHANNEL_SUCCEEDED = BASE + 73;
+
+
/**
* Create a new WifiP2pManager instance. Applications use
* {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve
@@ -667,6 +690,9 @@
case DELETE_PERSISTENT_GROUP_FAILED:
case SET_WFD_INFO_FAILED:
case START_WPS_FAILED:
+ case START_LISTEN_FAILED:
+ case STOP_LISTEN_FAILED:
+ case SET_CHANNEL_FAILED:
if (listener != null) {
((ActionListener) listener).onFailure(message.arg1);
}
@@ -689,6 +715,9 @@
case DELETE_PERSISTENT_GROUP_SUCCEEDED:
case SET_WFD_INFO_SUCCEEDED:
case START_WPS_SUCCEEDED:
+ case START_LISTEN_SUCCEEDED:
+ case STOP_LISTEN_SUCCEEDED:
+ case SET_CHANNEL_SUCCEEDED:
if (listener != null) {
((ActionListener) listener).onSuccess();
}
@@ -955,6 +984,22 @@
c.mAsyncChannel.sendMessage(REMOVE_GROUP, 0, c.putListener(listener));
}
+ /** @hide */
+ public void listen(Channel c, boolean enable, ActionListener listener) {
+ checkChannel(c);
+ c.mAsyncChannel.sendMessage(enable ? START_LISTEN : STOP_LISTEN,
+ 0, c.putListener(listener));
+ }
+
+ /** @hide */
+ public void setWifiP2pChannels(Channel c, int lc, int oc, ActionListener listener) {
+ checkChannel(c);
+ Bundle p2pChannels = new Bundle();
+ p2pChannels.putInt("lc", lc);
+ p2pChannels.putInt("oc", oc);
+ c.mAsyncChannel.sendMessage(SET_CHANNEL, 0, c.putListener(listener), p2pChannels);
+ }
+
/**
* Start a Wi-Fi Protected Setup (WPS) session.
*
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pService.java b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
index 63b94a2..05196b8 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pService.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
@@ -48,6 +48,7 @@
import android.net.wifi.p2p.nsd.WifiP2pServiceRequest;
import android.net.wifi.p2p.nsd.WifiP2pServiceResponse;
import android.os.Binder;
+import android.os.Bundle;
import android.os.IBinder;
import android.os.INetworkManagementService;
import android.os.Handler;
@@ -628,6 +629,9 @@
case DhcpStateMachine.CMD_ON_QUIT:
case WifiMonitor.P2P_PROV_DISC_FAILURE_EVENT:
case SET_MIRACAST_MODE:
+ case WifiP2pManager.START_LISTEN:
+ case WifiP2pManager.STOP_LISTEN:
+ case WifiP2pManager.SET_CHANNEL:
break;
case WifiStateMachine.CMD_ENABLE_P2P:
// Enable is lazy and has no response
@@ -729,7 +733,16 @@
replyToMessage(message, WifiP2pManager.START_WPS_FAILED,
WifiP2pManager.P2P_UNSUPPORTED);
break;
- default:
+ case WifiP2pManager.START_LISTEN:
+ replyToMessage(message, WifiP2pManager.START_LISTEN_FAILED,
+ WifiP2pManager.P2P_UNSUPPORTED);
+ break;
+ case WifiP2pManager.STOP_LISTEN:
+ replyToMessage(message, WifiP2pManager.STOP_LISTEN_FAILED,
+ WifiP2pManager.P2P_UNSUPPORTED);
+ break;
+
+ default:
return NOT_HANDLED;
}
return HANDLED;
@@ -1022,6 +1035,35 @@
case SET_MIRACAST_MODE:
mWifiNative.setMiracastMode(message.arg1);
break;
+ case WifiP2pManager.START_LISTEN:
+ if (DBG) logd(getName() + " start listen mode");
+ mWifiNative.p2pFlush();
+ if (mWifiNative.p2pExtListen(true, 500, 500)) {
+ replyToMessage(message, WifiP2pManager.START_LISTEN_SUCCEEDED);
+ } else {
+ replyToMessage(message, WifiP2pManager.START_LISTEN_FAILED);
+ }
+ break;
+ case WifiP2pManager.STOP_LISTEN:
+ if (DBG) logd(getName() + " stop listen mode");
+ if (mWifiNative.p2pExtListen(false, 0, 0)) {
+ replyToMessage(message, WifiP2pManager.STOP_LISTEN_SUCCEEDED);
+ } else {
+ replyToMessage(message, WifiP2pManager.STOP_LISTEN_FAILED);
+ }
+ mWifiNative.p2pFlush();
+ break;
+ case WifiP2pManager.SET_CHANNEL:
+ Bundle p2pChannels = (Bundle) message.obj;
+ int lc = p2pChannels.getInt("lc", 0);
+ int oc = p2pChannels.getInt("oc", 0);
+ if (DBG) logd(getName() + " set listen and operating channel");
+ if (mWifiNative.p2pSetChannel(lc, oc)) {
+ replyToMessage(message, WifiP2pManager.SET_CHANNEL_SUCCEEDED);
+ } else {
+ replyToMessage(message, WifiP2pManager.SET_CHANNEL_FAILED);
+ }
+ break;
default:
return NOT_HANDLED;
}
@@ -1171,6 +1213,35 @@
mWifiNative.p2pGroupRemove(mGroup.getInterface());
}
break;
+ case WifiP2pManager.START_LISTEN:
+ if (DBG) logd(getName() + " start listen mode");
+ mWifiNative.p2pFlush();
+ if (mWifiNative.p2pExtListen(true, 500, 500)) {
+ replyToMessage(message, WifiP2pManager.START_LISTEN_SUCCEEDED);
+ } else {
+ replyToMessage(message, WifiP2pManager.START_LISTEN_FAILED);
+ }
+ break;
+ case WifiP2pManager.STOP_LISTEN:
+ if (DBG) logd(getName() + " stop listen mode");
+ if (mWifiNative.p2pExtListen(false, 0, 0)) {
+ replyToMessage(message, WifiP2pManager.STOP_LISTEN_SUCCEEDED);
+ } else {
+ replyToMessage(message, WifiP2pManager.STOP_LISTEN_FAILED);
+ }
+ mWifiNative.p2pFlush();
+ break;
+ case WifiP2pManager.SET_CHANNEL:
+ Bundle p2pChannels = (Bundle) message.obj;
+ int lc = p2pChannels.getInt("lc", 0);
+ int oc = p2pChannels.getInt("oc", 0);
+ if (DBG) logd(getName() + " set listen and operating channel");
+ if (mWifiNative.p2pSetChannel(lc, oc)) {
+ replyToMessage(message, WifiP2pManager.SET_CHANNEL_SUCCEEDED);
+ } else {
+ replyToMessage(message, WifiP2pManager.SET_CHANNEL_FAILED);
+ }
+ break;
default:
return NOT_HANDLED;
}