Add app hibernation service stubs

Add app hibernation system service and manager class and start it when
app hibernation flag is enabled.

The actual API is currently stubbed and will be implemented in further
CLs.

Bug: 175829330
Test: adb shell device_config put app_hibernation
app_hibernation_enabled true
adb reboot
adb shell services list

Change-Id: Ide2758d3eaabae7d3df6356d4b6435c5137c554d
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 9f41c139..0f9ac4e 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -1409,6 +1409,15 @@
 
 }
 
+package android.apphibernation {
+
+  public final class AppHibernationManager {
+    method public boolean isHibernating(@NonNull String);
+    method public void setHibernating(@NonNull String, boolean);
+  }
+
+}
+
 package android.bluetooth {
 
   public final class BluetoothA2dp implements android.bluetooth.BluetoothProfile {
@@ -1680,6 +1689,7 @@
     method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public abstract void sendBroadcastAsUser(@RequiresPermission android.content.Intent, android.os.UserHandle, @Nullable String, @Nullable android.os.Bundle);
     method public abstract void sendOrderedBroadcast(@NonNull android.content.Intent, @Nullable String, @Nullable android.os.Bundle, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle);
     method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public void startActivityAsUser(@NonNull @RequiresPermission android.content.Intent, @NonNull android.os.UserHandle);
+    field public static final String APP_HIBERNATION_SERVICE = "app_hibernation";
     field public static final String APP_INTEGRITY_SERVICE = "app_integrity";
     field public static final String APP_PREDICTION_SERVICE = "app_prediction";
     field public static final String BACKUP_SERVICE = "backup";
diff --git a/core/java/android/apphibernation/AppHibernationManager.java b/core/java/android/apphibernation/AppHibernationManager.java
new file mode 100644
index 0000000..8f1934c
--- /dev/null
+++ b/core/java/android/apphibernation/AppHibernationManager.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2021 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.apphibernation;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
+/**
+ * This class provides an API surface for system apps to manipulate the app hibernation
+ * state of a package for the user provided in the context.
+ * @hide
+ */
+@SystemApi
+@SystemService(Context.APP_HIBERNATION_SERVICE)
+public final class AppHibernationManager {
+    private static final String TAG = "AppHibernationManager";
+    private final Context mContext;
+    private final IAppHibernationService mIAppHibernationService;
+
+    /**
+     * Creates a new instance.
+     *
+     * @param context The current context associated with the user
+     *
+     * @hide
+     */
+    public AppHibernationManager(@NonNull Context context) {
+        mContext = context;
+        mIAppHibernationService = IAppHibernationService.Stub.asInterface(
+                ServiceManager.getService(Context.APP_HIBERNATION_SERVICE));
+    }
+
+    /**
+     * Returns true if the package is hibernating, false otherwise.
+     *
+     * @hide
+     */
+    @SystemApi
+    public boolean isHibernating(@NonNull String packageName) {
+        try {
+            return mIAppHibernationService.isHibernating(packageName, mContext.getUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Set whether the package is hibernating.
+     *
+     * @hide
+     */
+    @SystemApi
+    public void setHibernating(@NonNull String packageName, boolean isHibernating) {
+        try {
+            mIAppHibernationService.setHibernating(packageName, mContext.getUserId(),
+                    isHibernating);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+}
diff --git a/core/java/android/apphibernation/IAppHibernationService.aidl b/core/java/android/apphibernation/IAppHibernationService.aidl
new file mode 100644
index 0000000..db57ecb
--- /dev/null
+++ b/core/java/android/apphibernation/IAppHibernationService.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2021 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.apphibernation;
+
+/**
+ * Binder interface to communicate with AppHibernationService.
+ * @hide
+ */
+interface IAppHibernationService {
+    boolean isHibernating(String packageName, int userId);
+    void setHibernating(String packageName, int userId, boolean isHibernating);
+}
\ No newline at end of file
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 92ede1c..7aafa4f6 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4509,6 +4509,17 @@
     public static final String PERMISSION_CONTROLLER_SERVICE = "permission_controller";
 
     /**
+     * Use with {@link #getSystemService(String) to retrieve an
+     * {@link android.apphibernation.AppHibernationManager}} for
+     * communicating with the hibernation service.
+     * @hide
+     *
+     * @see #getSystemService(String)
+     */
+    @SystemApi
+    public static final String APP_HIBERNATION_SERVICE = "app_hibernation";
+
+    /**
      * Use with {@link #getSystemService(String)} to retrieve an
      * {@link android.app.backup.IBackupManager IBackupManager} for communicating
      * with the backup mechanism.
diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationService.java b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
new file mode 100644
index 0000000..e8e83cc
--- /dev/null
+++ b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2021 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 com.android.server.apphibernation;
+
+import static android.provider.DeviceConfig.NAMESPACE_APP_HIBERNATION;
+
+import android.annotation.NonNull;
+import android.apphibernation.IAppHibernationService;
+import android.content.Context;
+import android.provider.DeviceConfig;
+
+import com.android.server.SystemService;
+
+/**
+ * System service that manages app hibernation state, a state apps can enter that means they are
+ * not being actively used and can be optimized for storage. The actual policy for determining
+ * if an app should hibernate is managed by PermissionController code.
+ */
+public final class AppHibernationService extends SystemService {
+
+    private final Context mContext;
+
+    /**
+     * Initializes the system service.
+     * <p>
+     * Subclasses must define a single argument constructor that accepts the context
+     * and passes it to super.
+     * </p>
+     *
+     * @param context The system server context.
+     */
+    public AppHibernationService(@NonNull Context context) {
+        super(context);
+        mContext = context;
+    }
+
+    @Override
+    public void onStart() {
+        publishBinderService(Context.APP_HIBERNATION_SERVICE, mServiceStub);
+    }
+
+    /**
+     * Whether a package is hibernating for a given user.
+     *
+     * @param packageName the package to check
+     * @param userId the user to check
+     * @return true if package is hibernating for the user
+     */
+    public boolean isHibernating(String packageName, int userId) {
+        // Stub
+        throw new UnsupportedOperationException("Hibernation state management not implemented yet");
+    }
+
+    /**
+     * Set whether the package is hibernating for the given user.
+     *
+     * @param packageName package to modify state
+     * @param userId user
+     * @param isHibernating new hibernation state
+     */
+    public void setHibernating(String packageName, int userId, boolean isHibernating) {
+        // Stub
+        throw new UnsupportedOperationException("Hibernation state management not implemented yet");
+    }
+
+    private final AppHibernationServiceStub mServiceStub = new AppHibernationServiceStub(this);
+
+    static final class AppHibernationServiceStub extends IAppHibernationService.Stub {
+        final AppHibernationService mService;
+
+        AppHibernationServiceStub(AppHibernationService service) {
+            mService = service;
+        }
+
+        @Override
+        public boolean isHibernating(String packageName, int userId) {
+            return mService.isHibernating(packageName, userId);
+        }
+
+        @Override
+        public void setHibernating(String packageName, int userId, boolean isHibernating) {
+            mService.setHibernating(packageName, userId, isHibernating);
+        }
+    }
+
+    /**
+     * Whether app hibernation is enabled on this device.
+     *
+     * @return true if enabled, false otherwise
+     */
+    public static boolean isAppHibernationEnabled() {
+        return DeviceConfig.getBoolean(
+                NAMESPACE_APP_HIBERNATION,
+                AppHibernationConstants.KEY_APP_HIBERNATION_ENABLED,
+                false /* defaultValue */);
+    }
+}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 4cd1348..92f7bb7 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -93,6 +93,7 @@
 import com.android.internal.widget.ILockSettings;
 import com.android.server.am.ActivityManagerService;
 import com.android.server.appbinding.AppBindingService;
+import com.android.server.apphibernation.AppHibernationService;
 import com.android.server.attention.AttentionManagerService;
 import com.android.server.audio.AudioService;
 import com.android.server.biometrics.AuthService;
@@ -215,6 +216,8 @@
             "com.android.server.appwidget.AppWidgetService";
     private static final String VOICE_RECOGNITION_MANAGER_SERVICE_CLASS =
             "com.android.server.voiceinteraction.VoiceInteractionManagerService";
+    private static final String APP_HIBERNATION_SERVICE_CLASS =
+            "com.android.server.apphibernation.AppHibernationService";
     private static final String PRINT_MANAGER_SERVICE_CLASS =
             "com.android.server.print.PrintManagerService";
     private static final String COMPANION_DEVICE_MANAGER_SERVICE_CLASS =
@@ -1798,6 +1801,12 @@
             mSystemServiceManager.startService(VOICE_RECOGNITION_MANAGER_SERVICE_CLASS);
             t.traceEnd();
 
+            if (AppHibernationService.isAppHibernationEnabled()) {
+                t.traceBegin("StartAppHibernationService");
+                mSystemServiceManager.startService(APP_HIBERNATION_SERVICE_CLASS);
+                t.traceEnd();
+            }
+
             if (GestureLauncherService.isGestureLauncherEnabled(context.getResources())) {
                 t.traceBegin("StartGestureLauncher");
                 mSystemServiceManager.startService(GestureLauncherService.class);