Add ExternalStorageService API
The ExternalStorageService implementation will live in android.process.media It will orchestrate filesystem IO from apps coming from an upper filesystem to a lower filesystem Test: m Bug: 135341433 Change-Id: I9b132ce7e5e5985ef3307c75ce7db50affc65a8e
This commit is contained in:
@@ -29,6 +29,7 @@ package android {
|
||||
field public static final String BIND_CONTENT_SUGGESTIONS_SERVICE = "android.permission.BIND_CONTENT_SUGGESTIONS_SERVICE";
|
||||
field public static final String BIND_DIRECTORY_SEARCH = "android.permission.BIND_DIRECTORY_SEARCH";
|
||||
field public static final String BIND_EUICC_SERVICE = "android.permission.BIND_EUICC_SERVICE";
|
||||
field public static final String BIND_EXTERNAL_STORAGE_SERVICE = "android.permission.BIND_EXTERNAL_STORAGE_SERVICE";
|
||||
field public static final String BIND_IMS_SERVICE = "android.permission.BIND_IMS_SERVICE";
|
||||
field public static final String BIND_KEYGUARD_APPWIDGET = "android.permission.BIND_KEYGUARD_APPWIDGET";
|
||||
field public static final String BIND_NETWORK_RECOMMENDATION_SERVICE = "android.permission.BIND_NETWORK_RECOMMENDATION_SERVICE";
|
||||
@@ -6767,6 +6768,20 @@ package android.service.sms {
|
||||
|
||||
}
|
||||
|
||||
package android.service.storage {
|
||||
|
||||
public abstract class ExternalStorageService extends android.app.Service {
|
||||
ctor public ExternalStorageService();
|
||||
method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
|
||||
method public abstract void onEndSession(@NonNull String) throws java.io.IOException;
|
||||
method public abstract void onStartSession(@NonNull String, int, @NonNull android.os.ParcelFileDescriptor, @NonNull String, @NonNull String) throws java.io.IOException;
|
||||
field public static final int FLAG_SESSION_ATTRIBUTE_INDEXABLE = 2; // 0x2
|
||||
field public static final int FLAG_SESSION_TYPE_FUSE = 1; // 0x1
|
||||
field public static final String SERVICE_INTERFACE = "android.service.storage.ExternalStorageService";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
package android.service.textclassifier {
|
||||
|
||||
public abstract class TextClassifierService extends android.app.Service {
|
||||
|
||||
168
core/java/android/service/storage/ExternalStorageService.java
Normal file
168
core/java/android/service/storage/ExternalStorageService.java
Normal file
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.service.storage;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.SdkConstant;
|
||||
import android.annotation.SystemApi;
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.ParcelableException;
|
||||
import android.os.RemoteCallback;
|
||||
import android.os.RemoteException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* A service to handle filesystem I/O from other apps.
|
||||
*
|
||||
* <p>To extend this class, you must declare the service in your manifest file with the
|
||||
* {@link android.Manifest.permission#BIND_EXTERNAL_STORAGE_SERVICE} permission,
|
||||
* and include an intent filter with the {@link #SERVICE_INTERFACE} action.
|
||||
* For example:</p>
|
||||
* <pre>
|
||||
* <service android:name=".ExternalStorageServiceImpl"
|
||||
* android:exported="true"
|
||||
* android:priority="100"
|
||||
* android:permission="android.permission.BIND_EXTERNAL_STORAGE_SERVICE">
|
||||
* <intent-filter>
|
||||
* <action android:name="android.service.storage.ExternalStorageService" />
|
||||
* </intent-filter>
|
||||
* </service>
|
||||
* </pre>
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public abstract class ExternalStorageService extends Service {
|
||||
/**
|
||||
* The Intent action that a service must respond to. Add it as an intent filter in the
|
||||
* manifest declaration of the implementing service.
|
||||
*/
|
||||
@SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
|
||||
public static final String SERVICE_INTERFACE = "android.service.storage.ExternalStorageService";
|
||||
/**
|
||||
* Whether the session associated with the device file descriptor when calling
|
||||
* {@link #onStartSession} is a FUSE session.
|
||||
*/
|
||||
public static final int FLAG_SESSION_TYPE_FUSE = 1 << 0;
|
||||
|
||||
/**
|
||||
* Whether the upper file system path specified when calling {@link #onStartSession}
|
||||
* should be indexed.
|
||||
*/
|
||||
public static final int FLAG_SESSION_ATTRIBUTE_INDEXABLE = 1 << 1;
|
||||
|
||||
/**
|
||||
* {@link Bundle} key for a {@link String} value.
|
||||
*
|
||||
* {@hide}
|
||||
*/
|
||||
public static final String EXTRA_SESSION_ID =
|
||||
"android.service.storage.extra.session_id";
|
||||
/**
|
||||
* {@link Bundle} key for a {@link ParcelableException} value.
|
||||
*
|
||||
* {@hide}
|
||||
*/
|
||||
public static final String EXTRA_ERROR =
|
||||
"android.service.storage.extra.error";
|
||||
|
||||
/** @hide */
|
||||
@IntDef(flag = true, prefix = {"FLAG_SESSION_"},
|
||||
value = {FLAG_SESSION_TYPE_FUSE, FLAG_SESSION_ATTRIBUTE_INDEXABLE})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface SessionFlag {}
|
||||
|
||||
private final ExternalStorageServiceWrapper mWrapper = new ExternalStorageServiceWrapper();
|
||||
private final Handler mHandler = new Handler(Looper.getMainLooper(), null, true);
|
||||
|
||||
/**
|
||||
* Called when the system starts a session associated with {@code deviceFd}
|
||||
* identified by {@code sessionId} to handle filesystem I/O for other apps. The type of
|
||||
* session and other attributes are passed in {@code flag}.
|
||||
*
|
||||
* <p> I/O is received as requests originating from {@code upperFileSystemPath} on
|
||||
* {@code deviceFd}. Implementors should handle the I/O by responding to these requests
|
||||
* using the data on the {@code lowerFileSystemPath}.
|
||||
*
|
||||
* <p> Additional calls to start a session for the same {@code sessionId} while the session
|
||||
* is still starting or already started should have no effect.
|
||||
*/
|
||||
public abstract void onStartSession(@NonNull String sessionId, @SessionFlag int flag,
|
||||
@NonNull ParcelFileDescriptor deviceFd, @NonNull String upperFileSystemPath,
|
||||
@NonNull String lowerFileSystemPath) throws IOException;
|
||||
|
||||
/**
|
||||
* Called when the system ends the session identified by {@code sessionId}. Implementors should
|
||||
* stop handling filesystem I/O and clean up resources from the ended session.
|
||||
*
|
||||
* <p> Additional calls to end a session for the same {@code sessionId} while the session
|
||||
* is still ending or has not started should have no effect.
|
||||
*/
|
||||
public abstract void onEndSession(@NonNull String sessionId) throws IOException;
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public final IBinder onBind(@NonNull Intent intent) {
|
||||
return mWrapper;
|
||||
}
|
||||
|
||||
private class ExternalStorageServiceWrapper extends IExternalStorageService.Stub {
|
||||
@Override
|
||||
public void startSession(String sessionId, @SessionFlag int flag,
|
||||
ParcelFileDescriptor deviceFd, String upperPath, String lowerPath,
|
||||
RemoteCallback callback) throws RemoteException {
|
||||
mHandler.post(() -> {
|
||||
try {
|
||||
onStartSession(sessionId, flag, deviceFd, upperPath, lowerPath);
|
||||
sendResult(sessionId, null /* throwable */, callback);
|
||||
} catch (Throwable t) {
|
||||
sendResult(sessionId, t, callback);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endSession(String sessionId, RemoteCallback callback) throws RemoteException {
|
||||
mHandler.post(() -> {
|
||||
try {
|
||||
onEndSession(sessionId);
|
||||
sendResult(sessionId, null /* throwable */, callback);
|
||||
} catch (Throwable t) {
|
||||
sendResult(sessionId, t, callback);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void sendResult(String sessionId, Throwable throwable, RemoteCallback callback) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString(EXTRA_SESSION_ID, sessionId);
|
||||
if (throwable != null) {
|
||||
bundle.putParcelable(EXTRA_ERROR, new ParcelableException(throwable));
|
||||
}
|
||||
callback.sendResult(bundle);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.service.storage;
|
||||
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.RemoteCallback;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
oneway interface IExternalStorageService
|
||||
{
|
||||
void startSession(@utf8InCpp String sessionId, int type, in ParcelFileDescriptor deviceFd,
|
||||
@utf8InCpp String upperPath, @utf8InCpp String lowerPath, in RemoteCallback callback);
|
||||
void endSession(@utf8InCpp String sessionId, in RemoteCallback callback);
|
||||
}
|
||||
@@ -4524,6 +4524,13 @@
|
||||
<permission android:name="android.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE"
|
||||
android:protectionLevel="signature|privileged" />
|
||||
|
||||
<!-- @SystemApi Must be required by an {@link android.service.storage.ExternalStorageService} to
|
||||
ensure that only the system can bind to it.
|
||||
@hide This is not a third-party API (intended for OEMs and system apps).
|
||||
-->
|
||||
<permission android:name="android.permission.BIND_EXTERNAL_STORAGE_SERVICE"
|
||||
android:protectionLevel="signature" />
|
||||
|
||||
<!-- @hide Permission that allows configuring appops.
|
||||
<p>Not for use by third-party applications. -->
|
||||
<permission android:name="android.permission.MANAGE_APPOPS"
|
||||
|
||||
Reference in New Issue
Block a user