update new sdk

This commit is contained in:
August 2020-07-15 19:27:51 +08:00
parent f33907443a
commit 744c72c133
1643 changed files with 83006 additions and 28021 deletions

View file

@ -20,7 +20,9 @@ import static android.provider.BaseColumns._ID;
import static android.provider.Downloads.Impl.COLUMN_DESTINATION;
import static android.provider.Downloads.Impl.COLUMN_MEDIA_SCANNED;
import static android.provider.Downloads.Impl.COLUMN_MIME_TYPE;
import static android.provider.Downloads.Impl.COLUMN_OTHER_UID;
import static android.provider.Downloads.Impl.DESTINATION_NON_DOWNLOADMANAGER_DOWNLOAD;
import static android.provider.Downloads.Impl.PERMISSION_ACCESS_ALL;
import static android.provider.Downloads.Impl._DATA;
import android.app.AppOpsManager;
@ -99,17 +101,14 @@ public final class DownloadProvider extends ContentProvider {
private static final int MY_DOWNLOADS = 1;
/** URI matcher constant for the URI of an individual download belonging to the calling UID */
private static final int MY_DOWNLOADS_ID = 2;
/** URI matcher constant for the URI of all downloads in the system */
private static final int ALL_DOWNLOADS = 3;
/** URI matcher constant for the URI of an individual download */
private static final int ALL_DOWNLOADS_ID = 4;
/** URI matcher constant for the URI of a download's request headers */
private static final int REQUEST_HEADERS_URI = 5;
/** URI matcher constant for the public URI returned by
* {@link DownloadManager#getUriForDownloadedFile(long)} if the given downloaded file
* is publicly accessible.
*/
private static final int PUBLIC_DOWNLOAD_ID = 6;
private static final int MY_DOWNLOADS_ID_HEADERS = 3;
/** URI matcher constant for the URI of all downloads in the system */
private static final int ALL_DOWNLOADS = 4;
/** URI matcher constant for the URI of an individual download */
private static final int ALL_DOWNLOADS_ID = 5;
/** URI matcher constant for the URI of a download's request headers */
private static final int ALL_DOWNLOADS_ID_HEADERS = 6;
static {
sURIMatcher.addURI("downloads", "my_downloads", MY_DOWNLOADS);
sURIMatcher.addURI("downloads", "my_downloads/#", MY_DOWNLOADS_ID);
@ -117,19 +116,16 @@ public final class DownloadProvider extends ContentProvider {
sURIMatcher.addURI("downloads", "all_downloads/#", ALL_DOWNLOADS_ID);
sURIMatcher.addURI("downloads",
"my_downloads/#/" + Downloads.Impl.RequestHeaders.URI_SEGMENT,
REQUEST_HEADERS_URI);
MY_DOWNLOADS_ID_HEADERS);
sURIMatcher.addURI("downloads",
"all_downloads/#/" + Downloads.Impl.RequestHeaders.URI_SEGMENT,
REQUEST_HEADERS_URI);
ALL_DOWNLOADS_ID_HEADERS);
// temporary, for backwards compatibility
sURIMatcher.addURI("downloads", "download", MY_DOWNLOADS);
sURIMatcher.addURI("downloads", "download/#", MY_DOWNLOADS_ID);
sURIMatcher.addURI("downloads",
"download/#/" + Downloads.Impl.RequestHeaders.URI_SEGMENT,
REQUEST_HEADERS_URI);
sURIMatcher.addURI("downloads",
Downloads.Impl.PUBLICLY_ACCESSIBLE_DOWNLOADS_URI_SEGMENT + "/#",
PUBLIC_DOWNLOAD_ID);
MY_DOWNLOADS_ID_HEADERS);
}
/** Different base URIs that could be used to access an individual download */
@ -191,43 +187,6 @@ public final class DownloadProvider extends ContentProvider {
private int mSystemUid = -1;
private int mDefContainerUid = -1;
/**
* This class encapsulates a SQL where clause and its parameters. It makes it possible for
* shared methods (like {@link DownloadProvider#getWhereClause(Uri, String, String[], int)})
* to return both pieces of information, and provides some utility logic to ease piece-by-piece
* construction of selections.
*/
private static class SqlSelection {
public StringBuilder mWhereClause = new StringBuilder();
public List<String> mParameters = new ArrayList<String>();
public <T> void appendClause(String newClause, final T... parameters) {
if (newClause == null || newClause.isEmpty()) {
return;
}
if (mWhereClause.length() != 0) {
mWhereClause.append(" AND ");
}
mWhereClause.append("(");
mWhereClause.append(newClause);
mWhereClause.append(")");
if (parameters != null) {
for (Object parameter : parameters) {
mParameters.add(parameter.toString());
}
}
}
public String getSelection() {
return mWhereClause.toString();
}
public String[] getParameters() {
String[] array = new String[mParameters.size()];
return mParameters.toArray(array);
}
}
/**
* Creates and updated database on demand when opening it.
* Helper class to create database the first time the provider is
@ -526,8 +485,7 @@ public final class DownloadProvider extends ContentProvider {
return DOWNLOAD_LIST_TYPE;
}
case MY_DOWNLOADS_ID:
case ALL_DOWNLOADS_ID:
case PUBLIC_DOWNLOAD_ID: {
case ALL_DOWNLOADS_ID: {
// return the mimetype of this id from the database
final String id = getDownloadIdFromUri(uri);
final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
@ -942,15 +900,23 @@ public final class DownloadProvider extends ContentProvider {
throw new IllegalArgumentException("Unknown URI: " + uri);
}
if (match == REQUEST_HEADERS_URI) {
if (match == MY_DOWNLOADS_ID_HEADERS || match == ALL_DOWNLOADS_ID_HEADERS) {
if (projection != null || selection != null || sort != null) {
throw new UnsupportedOperationException("Request header queries do not support "
+ "projections, selections or sorting");
}
return queryRequestHeaders(db, uri);
}
SqlSelection fullSelection = getWhereClause(uri, selection, selectionArgs, match);
// Headers are only available to callers with full access.
getContext().enforceCallingOrSelfPermission(
Downloads.Impl.PERMISSION_ACCESS_ALL, Constants.TAG);
final SQLiteQueryBuilder qb = getQueryBuilder(uri, match);
projection = new String[] {
Downloads.Impl.RequestHeaders.COLUMN_HEADER,
Downloads.Impl.RequestHeaders.COLUMN_VALUE
};
return qb.query(db, projection, null, null, null, null, null);
}
if (shouldRestrictVisibility()) {
if (projection == null) {
@ -978,11 +944,8 @@ public final class DownloadProvider extends ContentProvider {
logVerboseQueryInfo(projection, selection, selectionArgs, sort, db);
}
SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
builder.setTables(DB_TABLE);
builder.setStrict(true);
Cursor ret = builder.query(db, projection, fullSelection.getSelection(),
fullSelection.getParameters(), null, null, sort);
final SQLiteQueryBuilder qb = getQueryBuilder(uri, match);
final Cursor ret = qb.query(db, projection, selection, selectionArgs, null, null, sort);
if (ret != null) {
ret.setNotificationUri(getContext().getContentResolver(), uri);
@ -1067,35 +1030,6 @@ public final class DownloadProvider extends ContentProvider {
}
}
/**
* Handle a query for the custom request headers registered for a download.
*/
private Cursor queryRequestHeaders(SQLiteDatabase db, Uri uri) {
String where = Downloads.Impl.RequestHeaders.COLUMN_DOWNLOAD_ID + "="
+ getDownloadIdFromUri(uri);
String[] projection = new String[] {Downloads.Impl.RequestHeaders.COLUMN_HEADER,
Downloads.Impl.RequestHeaders.COLUMN_VALUE};
return db.query(Downloads.Impl.RequestHeaders.HEADERS_DB_TABLE, projection, where,
null, null, null, null);
}
/**
* Delete request headers for downloads matching the given query.
*/
private void deleteRequestHeaders(SQLiteDatabase db, String where, String[] whereArgs) {
String[] projection = new String[] {Downloads.Impl._ID};
Cursor cursor = db.query(DB_TABLE, projection, where, whereArgs, null, null, null, null);
try {
for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
long id = cursor.getLong(0);
String idWhere = Downloads.Impl.RequestHeaders.COLUMN_DOWNLOAD_ID + "=" + id;
db.delete(Downloads.Impl.RequestHeaders.HEADERS_DB_TABLE, idWhere, null);
}
} finally {
cursor.close();
}
}
/**
* @return true if we should restrict the columns readable by this caller
*/
@ -1178,13 +1112,12 @@ public final class DownloadProvider extends ContentProvider {
break;
}
final SqlSelection selection = getWhereClause(uri, where, whereArgs, match);
count = db.update(DB_TABLE, filteredValues, selection.getSelection(),
selection.getParameters());
final SQLiteQueryBuilder qb = getQueryBuilder(uri, match);
count = qb.update(db, filteredValues, where, whereArgs);
if (updateSchedule || isCompleting) {
final long token = Binder.clearCallingIdentity();
try (Cursor cursor = db.query(DB_TABLE, null, selection.getSelection(),
selection.getParameters(), null, null, null)) {
try (Cursor cursor = qb.query(db, null, where, whereArgs,
null, null, null)) {
final DownloadInfo.Reader reader = new DownloadInfo.Reader(resolver,
cursor);
final DownloadInfo info = new DownloadInfo(context);
@ -1230,22 +1163,64 @@ public final class DownloadProvider extends ContentProvider {
}
}
private SqlSelection getWhereClause(final Uri uri, final String where, final String[] whereArgs,
int uriMatch) {
SqlSelection selection = new SqlSelection();
selection.appendClause(where, whereArgs);
if (uriMatch == MY_DOWNLOADS_ID || uriMatch == ALL_DOWNLOADS_ID ||
uriMatch == PUBLIC_DOWNLOAD_ID) {
selection.appendClause(Downloads.Impl._ID + " = ?", getDownloadIdFromUri(uri));
/**
* Create a query builder that filters access to the underlying database
* based on both the requested {@link Uri} and permissions of the caller.
*/
private SQLiteQueryBuilder getQueryBuilder(final Uri uri, int match) {
final String table;
final StringBuilder where = new StringBuilder();
switch (match) {
// The "my_downloads" view normally limits the caller to operating
// on downloads that they either directly own, or have been given
// indirect ownership of via OTHER_UID.
case MY_DOWNLOADS_ID:
appendWhereExpression(where, _ID + "=" + getDownloadIdFromUri(uri));
// fall-through
case MY_DOWNLOADS:
table = DB_TABLE;
if (getContext().checkCallingOrSelfPermission(
PERMISSION_ACCESS_ALL) != PackageManager.PERMISSION_GRANTED) {
appendWhereExpression(where, Constants.UID + "=" + Binder.getCallingUid()
+ " OR " + COLUMN_OTHER_UID + "=" + Binder.getCallingUid());
}
break;
// The "all_downloads" view is already limited via <path-permission>
// to only callers holding the ACCESS_ALL_DOWNLOADS permission, but
// access may also be delegated via Uri permission grants.
case ALL_DOWNLOADS_ID:
appendWhereExpression(where, _ID + "=" + getDownloadIdFromUri(uri));
// fall-through
case ALL_DOWNLOADS:
table = DB_TABLE;
break;
// Headers are limited to callers holding the ACCESS_ALL_DOWNLOADS
// permission, since they're only needed for executing downloads.
case MY_DOWNLOADS_ID_HEADERS:
case ALL_DOWNLOADS_ID_HEADERS:
table = Downloads.Impl.RequestHeaders.HEADERS_DB_TABLE;
appendWhereExpression(where, Downloads.Impl.RequestHeaders.COLUMN_DOWNLOAD_ID + "="
+ getDownloadIdFromUri(uri));
break;
default:
throw new UnsupportedOperationException("Unknown URI: " + uri);
}
if ((uriMatch == MY_DOWNLOADS || uriMatch == MY_DOWNLOADS_ID)
&& getContext().checkCallingOrSelfPermission(Downloads.Impl.PERMISSION_ACCESS_ALL)
!= PackageManager.PERMISSION_GRANTED) {
selection.appendClause(
Constants.UID + "= ? OR " + Downloads.Impl.COLUMN_OTHER_UID + "= ?",
Binder.getCallingUid(), Binder.getCallingUid());
final SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
qb.setStrict(true);
qb.setTables(table);
qb.appendWhere(where);
return qb;
}
private static void appendWhereExpression(StringBuilder sb, String expression) {
if (sb.length() > 0) {
sb.append(" AND ");
}
return selection;
sb.append('(').append(expression).append(')');
}
/**
@ -1269,11 +1244,8 @@ public final class DownloadProvider extends ContentProvider {
case MY_DOWNLOADS_ID:
case ALL_DOWNLOADS:
case ALL_DOWNLOADS_ID:
final SqlSelection selection = getWhereClause(uri, where, whereArgs, match);
deleteRequestHeaders(db, selection.getSelection(), selection.getParameters());
try (Cursor cursor = db.query(DB_TABLE, null, selection.getSelection(),
selection.getParameters(), null, null, null)) {
final SQLiteQueryBuilder qb = getQueryBuilder(uri, match);
try (Cursor cursor = qb.query(db, null, where, whereArgs, null, null, null)) {
final DownloadInfo.Reader reader = new DownloadInfo.Reader(resolver, cursor);
final DownloadInfo info = new DownloadInfo(context);
while (cursor.moveToNext()) {
@ -1315,10 +1287,15 @@ public final class DownloadProvider extends ContentProvider {
if (!Downloads.Impl.isStatusCompleted(info.mStatus)) {
info.sendIntentIfRequested();
}
// Delete any headers for this download
db.delete(Downloads.Impl.RequestHeaders.HEADERS_DB_TABLE,
Downloads.Impl.RequestHeaders.COLUMN_DOWNLOAD_ID + "=?",
new String[] { Long.toString(info.mId) });
}
}
count = db.delete(DB_TABLE, selection.getSelection(), selection.getParameters());
count = qb.delete(db, where, whereArgs);
break;
default:

View file

@ -725,7 +725,7 @@ public class DownloadThread extends Thread {
if (info.isRoaming() && !mInfo.isRoamingAllowed()) {
throw new StopRequestException(STATUS_WAITING_FOR_NETWORK, "Network is roaming");
}
if (mSystemFacade.isNetworkMetered(mNetwork)
if (mSystemFacade.isActiveNetworkMeteredForUid(mInfo.mUid)
&& !mInfo.isMeteredAllowed(mInfoDelta.mTotalBytes)) {
throw new StopRequestException(STATUS_WAITING_FOR_NETWORK, "Network is metered");
}

View file

@ -67,6 +67,12 @@ class RealSystemFacade implements SystemFacade {
.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
}
@Override
public boolean isActiveNetworkMeteredForUid(int uid) {
return mContext.getSystemService(ConnectivityManager.class)
.isActiveNetworkMeteredForUid(uid);
}
@Override
public long getMaxBytesOverMobile() {
final Long value = DownloadManager.getMaxBytesOverMobile(mContext);

View file

@ -38,6 +38,8 @@ interface SystemFacade {
public boolean isNetworkMetered(Network network);
public boolean isActiveNetworkMeteredForUid(int uid);
/**
* @return maximum size, in bytes, of downloads that may go over a mobile connection; or null if
* there's no limit

View file

@ -101,6 +101,11 @@ public class FakeSystemFacade implements SystemFacade {
return mIsMetered;
}
@Override
public boolean isActiveNetworkMeteredForUid(int uid) {
return mIsMetered;
}
@Override
public long getMaxBytesOverMobile() {
return mMaxBytesOverMobile;

View file

@ -152,6 +152,11 @@ public class UserDictionaryProvider extends ContentProvider {
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
// Only the enabled IMEs and spell checkers can access this provider.
if (!canCallerAccessUserDictionary()) {
return getEmptyCursorOrThrow(projection);
}
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
switch (sUriMatcher.match(uri)) {
@ -170,11 +175,6 @@ public class UserDictionaryProvider extends ContentProvider {
throw new IllegalArgumentException("Unknown URI " + uri);
}
// Only the enabled IMEs and spell checkers can access this provider.
if (!canCallerAccessUserDictionary()) {
return getEmptyCursorOrThrow(projection);
}
// If no sort order is specified use the default
String orderBy;
if (TextUtils.isEmpty(sortOrder)) {
@ -257,6 +257,11 @@ public class UserDictionaryProvider extends ContentProvider {
@Override
public int delete(Uri uri, String where, String[] whereArgs) {
// Only the enabled IMEs and spell checkers can access this provider.
if (!canCallerAccessUserDictionary()) {
return 0;
}
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
int count;
switch (sUriMatcher.match(uri)) {
@ -274,11 +279,6 @@ public class UserDictionaryProvider extends ContentProvider {
throw new IllegalArgumentException("Unknown URI " + uri);
}
// Only the enabled IMEs and spell checkers can access this provider.
if (!canCallerAccessUserDictionary()) {
return 0;
}
getContext().getContentResolver().notifyChange(uri, null);
mBackupManager.dataChanged();
return count;
@ -286,6 +286,11 @@ public class UserDictionaryProvider extends ContentProvider {
@Override
public int update(Uri uri, ContentValues values, String where, String[] whereArgs) {
// Only the enabled IMEs and spell checkers can access this provider.
if (!canCallerAccessUserDictionary()) {
return 0;
}
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
int count;
switch (sUriMatcher.match(uri)) {
@ -303,11 +308,6 @@ public class UserDictionaryProvider extends ContentProvider {
throw new IllegalArgumentException("Unknown URI " + uri);
}
// Only the enabled IMEs and spell checkers can access this provider.
if (!canCallerAccessUserDictionary()) {
return 0;
}
getContext().getContentResolver().notifyChange(uri, null);
mBackupManager.dataChanged();
return count;