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

Commit 31b4834b authored by Christopher Tate's avatar Christopher Tate Committed by Android (Google) Code Review
Browse files

Merge "Introduce "IdleService" API to expose idle-time maintenance to apps"

parents ef83738d d417d625
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -89,6 +89,8 @@ LOCAL_SRC_FILES += \
	core/java/android/app/backup/IFullBackupRestoreObserver.aidl \
	core/java/android/app/backup/IRestoreObserver.aidl \
	core/java/android/app/backup/IRestoreSession.aidl \
	core/java/android/app/maintenance/IIdleCallback.aidl \
	core/java/android/app/maintenance/IIdleService.aidl \
	core/java/android/bluetooth/IBluetooth.aidl \
	core/java/android/bluetooth/IBluetoothA2dp.aidl \
	core/java/android/bluetooth/IBluetoothCallback.aidl \
+14 −0
Original line number Diff line number Diff line
@@ -4886,6 +4886,20 @@ package android.app.backup {
}
package android.app.maintenance {
  public abstract class IdleService extends android.app.Service {
    ctor public IdleService();
    method public final void finishIdle();
    method public final android.os.IBinder onBind(android.content.Intent);
    method public abstract boolean onIdleStart();
    method public abstract void onIdleStop();
    field public static final java.lang.String PERMISSION_BIND = "android.permission.BIND_IDLE_SERVICE";
    field public static final java.lang.String SERVICE_INTERFACE = "android.service.idle.IdleService";
  }
}
package android.appwidget {
  public class AppWidgetHost {
+53 −0
Original line number Diff line number Diff line
/**
 * Copyright 2014, 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.app.maintenance;

import android.app.maintenance.IIdleService;

/**
 * The server side of the idle maintenance IPC protocols.  The app-side implementation
 * invokes on this interface to indicate completion of the (asynchronous) instructions
 * issued by the server.
 *
 * In all cases, the 'who' parameter is the caller's service binder, used to track
 * which idle service instance is reporting.
 *
 * {@hide}
 */
interface IIdleCallback {
    /**
     * Acknowledge receipt and processing of the asynchronous "start idle work" incall.
     * 'result' is true if the app wants some time to perform ongoing background
     * idle-time work; or false if the app declares that it does not need any time
     * for such work.
     */
    void acknowledgeStart(int token, boolean result);

    /**
     * Acknowledge receipt and processing of the asynchronous "stop idle work" incall.
     */
    void acknowledgeStop(int token);

    /*
     * Tell the idle service manager that we're done with our idle maintenance, so that
     * it can go on to the next one and stop attributing wakelock time to us etc.
     *
     * @param opToken The identifier passed in the startIdleMaintenance() call that
     *        indicated the beginning of this service's idle timeslice.
     */
    void idleFinished(int token);
}
+34 −0
Original line number Diff line number Diff line
/**
 * Copyright 2014, 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.app.maintenance;

import android.app.maintenance.IIdleCallback;

/**
 * Interface that the framework uses to communicate with application code
 * that implements an idle-time "maintenance" service.  End user code does
 * not implement this interface directly; instead, the app's idle service
 * implementation will extend android.app.maintenance.IdleService.
 * {@hide}
 */
oneway interface IIdleService {
    /**
     * Begin your idle-time work.
     */
    void startIdleMaintenance(IIdleCallback callbackBinder, int token);
    void stopIdleMaintenance(IIdleCallback callbackBinder, int token);
}
+228 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2014 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.app.maintenance;

import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
import android.util.Slog;

/**
 * Idle maintenance API.  Full docs TBW (to be written).
 */
public abstract class IdleService extends Service {
    private static final String TAG = "IdleService";

    static final int MSG_START = 1;
    static final int MSG_STOP = 2;
    static final int MSG_FINISH = 3;

    IdleHandler mHandler;
    IIdleCallback mCallbackBinder;
    int mToken;
    final Object mHandlerLock = new Object();

    void ensureHandler() {
        synchronized (mHandlerLock) {
            if (mHandler == null) {
                mHandler = new IdleHandler(getMainLooper());
            }
        }
    }

    /**
     * TBW: the idle service should supply an intent-filter handling this intent
     * <p>
     * <p class="note">The application must also protect the idle service with the
     * {@code "android.permission.BIND_IDLE_SERVICE"} permission to ensure that other
     * applications cannot maliciously bind to it.  If an idle service's manifest
     * declaration does not require that permission, it will never be invoked.
     * </p>
     */
    @SdkConstant(SdkConstantType.SERVICE_ACTION)
    public static final String SERVICE_INTERFACE =
            "android.service.idle.IdleService";

    /**
     * Idle services must be protected with this permission:
     *
     * <pre class="prettyprint">
     *     <service android:name="MyIdleService"
     *              android:permission="android.permission.BIND_IDLE_SERVICE" >
     *         ...
     *     </service>
     * </pre>
     *
     * <p>If an idle service is declared in the manifest but not protected with this
     * permission, that service will be ignored by the OS.
     */
    public static final String PERMISSION_BIND =
            "android.permission.BIND_IDLE_SERVICE";

    // Trampoline: the callbacks are always run on the main thread
    IIdleService mBinder = new IIdleService.Stub() {
        @Override
        public void startIdleMaintenance(IIdleCallback callbackBinder, int token)
                throws RemoteException {
            ensureHandler();
            Message msg = mHandler.obtainMessage(MSG_START, token, 0, callbackBinder);
            mHandler.sendMessage(msg);
        }

        @Override
        public void stopIdleMaintenance(IIdleCallback callbackBinder, int token)
                throws RemoteException {
            ensureHandler();
            Message msg = mHandler.obtainMessage(MSG_STOP, token, 0, callbackBinder);
            mHandler.sendMessage(msg);
        }
    };

    /**
     * Your application may begin doing "idle" maintenance work in the background.
     * <p>
     * Your application may continue to run in the background until it receives a call
     * to {@link #onIdleStop()}, at which point you <i>must</i> cease doing work.  The
     * OS will hold a wakelock on your application's behalf from the time this method is
     * called until after the following call to {@link #onIdleStop()} returns.
     * </p>
     * <p>
     * Returning {@code false} from this method indicates that you have no ongoing work
     * to do at present.  The OS will respond by immediately calling {@link #onIdleStop()}
     * and returning your application to its normal stopped state.  Returning {@code true}
     * indicates that the application is indeed performing ongoing work, so the OS will
     * let your application run in this state until it's no longer appropriate.
     * </p>
     * <p>
     * You will always receive a matching call to {@link #onIdleStop()} even if your
     * application returns {@code false} from this method.
     *
     * @return {@code true} to indicate that the application wishes to perform some ongoing
     *     background work; {@code false} to indicate that it does not need to perform such
     *     work at present.
     */
    public abstract boolean onIdleStart();

    /**
     * Your app's maintenance opportunity is over.  Once the application returns from
     * this method, the wakelock held by the OS on its behalf will be released.
     */
    public abstract void onIdleStop();

    /**
     * Tell the OS that you have finished your idle work.  Calling this more than once,
     * or calling it when you have not received an {@link #onIdleStart()} callback, is
     * an error.
     *
     * <p>It is safe to call {@link #finishIdle()} from any thread.
     */
    public final void finishIdle() {
        ensureHandler();
        mHandler.sendEmptyMessage(MSG_FINISH);
    }

    class IdleHandler extends Handler {
        IdleHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_START: {
                    // Call the concrete onIdleStart(), reporting its return value back to
                    // the OS.  If onIdleStart() throws, report it as a 'false' return but
                    // rethrow the exception at the offending app.
                    boolean result = false;
                    IIdleCallback callbackBinder = (IIdleCallback) msg.obj;
                    mCallbackBinder = callbackBinder;
                    final int token = mToken = msg.arg1;
                    try {
                        result = IdleService.this.onIdleStart();
                    } catch (Exception e) {
                        Log.e(TAG, "Unable to start idle workload", e);
                        throw new RuntimeException(e);
                    } finally {
                        // don't bother if the service already called finishIdle()
                        if (mCallbackBinder != null) {
                            try {
                                callbackBinder.acknowledgeStart(token, result);
                            } catch (RemoteException re) {
                                Log.e(TAG, "System unreachable to start idle workload");
                            }
                        }
                    }
                    break;
                }

                case MSG_STOP: {
                    // Structured just like MSG_START for the stop-idle bookend call.
                    IIdleCallback callbackBinder = (IIdleCallback) msg.obj;
                    final int token = msg.arg1;
                    try {
                        IdleService.this.onIdleStop();
                    } catch (Exception e) {
                        Log.e(TAG, "Unable to stop idle workload", e);
                        throw new RuntimeException(e);
                    } finally {
                        if (mCallbackBinder != null) {
                            try {
                                callbackBinder.acknowledgeStop(token);
                            } catch (RemoteException re) {
                                Log.e(TAG, "System unreachable to stop idle workload");
                            }
                        }
                    }
                    break;
                }

                case MSG_FINISH: {
                    if (mCallbackBinder != null) {
                        try {
                            mCallbackBinder.idleFinished(mToken);
                        } catch (RemoteException e) {
                            Log.e(TAG, "System unreachable to finish idling");
                        } finally {
                            mCallbackBinder = null;
                        }
                    } else {
                        Log.e(TAG, "finishIdle() called but the idle service is not started");
                    }
                    break;
                }

                default: {
                    Slog.w(TAG, "Unknown message " + msg.what);
                }
            }
        }
    }

    /** @hide */
    @Override
    public final IBinder onBind(Intent intent) {
        return mBinder.asBinder();
    }

}
Loading