Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 74c76a2c authored by Zimuzo Ezeozue's avatar Zimuzo Ezeozue Committed by Android (Google) Code Review
Browse files

Merge "Add ExternalStorageService API"

parents 14275a93 e1c49ce3
Loading
Loading
Loading
Loading
+15 −0
Original line number Diff line number Diff line
@@ -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 −0
Original line number Diff line number Diff line
/*
 * 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>
 *     &lt;service android:name=".ExternalStorageServiceImpl"
 *             android:exported="true"
 *             android:priority="100"
 *             android:permission="android.permission.BIND_EXTERNAL_STORAGE_SERVICE"&gt;
 *         &lt;intent-filter&gt;
 *             &lt;action android:name="android.service.storage.ExternalStorageService" /&gt;
 *         &lt;/intent-filter&gt;
 *     &lt;/service&gt;
 * </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);
        }
    }
}
+30 −0
Original line number Diff line number Diff line
/*
 * 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);
}
 No newline at end of file
+7 −0
Original line number Diff line number Diff line
@@ -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"