Merge "Implement display/voice interaction mediator" into sc-dev
diff --git a/service/src/com/android/car/power/PowerComponentHandler.java b/service/src/com/android/car/power/PowerComponentHandler.java
index 5d1c6cb..4598cb4 100644
--- a/service/src/com/android/car/power/PowerComponentHandler.java
+++ b/service/src/com/android/car/power/PowerComponentHandler.java
@@ -29,6 +29,8 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.net.wifi.WifiManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.util.AtomicFile;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -36,6 +38,7 @@
 
 import com.android.car.systeminterface.SystemInterface;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.IVoiceInteractionManagerService;
 
 import java.io.BufferedReader;
 import java.io.BufferedWriter;
@@ -59,12 +62,14 @@
             "forced_off_components";
 
     private final Context mContext;
+    private final SystemInterface mSystemInterface;
     private final AtomicFile mComponentStateFile;
     private final SparseArray<PowerComponentMediator> mPowerComponentMediators =
             new SparseArray<>();
 
     PowerComponentHandler(Context context, SystemInterface systemInterface) {
         mContext = context;
+        mSystemInterface = systemInterface;
         mComponentStateFile = new AtomicFile(new File(systemInterface.getSystemCarDir(),
                 FORCED_OFF_COMPONENTS_FILENAME));
     }
@@ -214,25 +219,28 @@
         }
     }
 
-    private final class AudioPowerComponentMediator extends PowerComponentMediator {
-        AudioPowerComponentMediator() {
-            super(PowerComponent.AUDIO);
-        }
-        // TODO(b/162600135): implement turning on/off audio.
-    }
-
-    private final class MediaPowerComponentMediator extends PowerComponentMediator {
-        MediaPowerComponentMediator() {
-            super(PowerComponent.MEDIA);
-        }
-        // TODO(b/162600135): implement turning on/off media.
-    }
-
+    // TODO(b/178824607): Check if power policy can turn on/off display as quickly as the existing
+    // implementation.
     private final class DisplayPowerComponentMediator extends PowerComponentMediator {
         DisplayPowerComponentMediator() {
             super(PowerComponent.DISPLAY);
         }
-        // TODO(b/162600135): implement turning on/off display.
+
+        @Override
+        public boolean isComponentAvailable() {
+            // It is assumed that display is supported in all vehicles.
+            return true;
+        }
+
+        @Override
+        public void setEnabled(boolean enabled) {
+            mSystemInterface.setDisplayState(enabled);
+        }
+
+        @Override
+        public boolean isEnabled() {
+            return mSystemInterface.isDisplayEnabled();
+        }
     }
 
     private final class WifiPowerComponentMediator extends PowerComponentMediator {
@@ -243,82 +251,57 @@
             mWifiManager = mContext.getSystemService(WifiManager.class);
         }
 
+        @Override
         public boolean isComponentAvailable() {
             PackageManager pm = mContext.getPackageManager();
             return pm.hasSystemFeature(PackageManager.FEATURE_WIFI);
         }
 
+        @Override
         public void setEnabled(boolean enabled) {
             mWifiManager.setWifiEnabled(enabled);
         }
 
+        @Override
         public boolean isEnabled() {
             return mWifiManager.isWifiEnabled();
         }
     }
 
-    private final class CellularPowerComponentMediator extends PowerComponentMediator {
-        CellularPowerComponentMediator() {
-            super(PowerComponent.CELLULAR);
-        }
-        // TODO(b/162600135): implement turning on/off cellular.
-    }
-
-    private final class EthernetPowerComponentMediator extends PowerComponentMediator {
-        EthernetPowerComponentMediator() {
-            super(PowerComponent.ETHERNET);
-        }
-        // TODO(b/162600135): implement turning on/off ethernet.
-    }
-
-    private final class ProjectionPowerComponentMediator extends PowerComponentMediator {
-        ProjectionPowerComponentMediator() {
-            super(PowerComponent.PROJECTION);
-        }
-        // TODO(b/162600135): implement turning on/off projection.
-    }
-
-    private final class NfcPowerComponentMediator extends PowerComponentMediator {
-        NfcPowerComponentMediator() {
-            super(PowerComponent.NFC);
-        }
-        // TODO(b/162600135): implement turning on/off nfc.
-    }
-
-    private final class InputPowerComponentMediator extends PowerComponentMediator {
-        InputPowerComponentMediator() {
-            super(PowerComponent.INPUT);
-        }
-        // TODO(b/162600135): implement turning on/off input.
-    }
-
     private final class VoiceInteractionPowerComponentMediator extends PowerComponentMediator {
+        private final IVoiceInteractionManagerService mVoiceInteractionManagerService;
+
+        private boolean mIsEnabled = true;
+
         VoiceInteractionPowerComponentMediator() {
             super(PowerComponent.VOICE_INTERACTION);
+            mVoiceInteractionManagerService = IVoiceInteractionManagerService.Stub.asInterface(
+                        ServiceManager.getService(Context.VOICE_INTERACTION_MANAGER_SERVICE));
         }
-        // TODO(b/162600135): implement turning on/off voice interaction.
-    }
 
-    private final class VisualInteractionPowerComponentMediator extends PowerComponentMediator {
-        VisualInteractionPowerComponentMediator() {
-            super(PowerComponent.VISUAL_INTERACTION);
+        @Override
+        public boolean isComponentAvailable() {
+            return mVoiceInteractionManagerService != null;
         }
-        // TODO(b/162600135): implement turning on/off visual interaction.
-    }
 
-    private final class TrustedDeviceDetectionPowerComponentMediator
-            extends PowerComponentMediator {
-        TrustedDeviceDetectionPowerComponentMediator() {
-            super(PowerComponent.TRUSTED_DEVICE_DETECTION);
+        @Override
+        public void setEnabled(boolean enabled) {
+            try {
+                mVoiceInteractionManagerService.setDisabled(!enabled);
+                mIsEnabled = enabled;
+            } catch (RemoteException e) {
+                Slog.w(TAG, "IVoiceInteractionManagerService.setDisabled(" + !enabled + ") failed",
+                        e);
+            }
         }
-        // TODO(b/162600135): implement turning on/off trusted device detection.
-    }
 
-    private final class MicroPhonePowerComponentMediator extends PowerComponentMediator {
-        MicroPhonePowerComponentMediator() {
-            super(PowerComponent.MICROPHONE);
+        @Override
+        public boolean isEnabled() {
+            // IVoiceInteractionManagerService doesn't have a method to tell enabled state. Assuming
+            // voice interaction is controlled only by AAOS CPMS, it tracks the state internally.
+            // TODO(b/178504489): Add isEnabled to IVoiceInterctionManagerService and use it.
+            return mIsEnabled;
         }
-        // TODO(b/162600135): implement turning on/off microphone.
     }
 
     private final class PowerComponentMediatorFactory {
@@ -326,31 +309,35 @@
         PowerComponentMediator createPowerComponent(int component) {
             switch (component) {
                 case PowerComponent.AUDIO:
-                    return new PowerComponentHandler.AudioPowerComponentMediator();
+                    // We don't control audio in framework level, because audio is enabled or
+                    // disabled in audio HAL according to the current power policy.
+                    return null;
                 case PowerComponent.MEDIA:
-                    return new MediaPowerComponentMediator();
+                    return null;
                 case PowerComponent.DISPLAY:
                     return new DisplayPowerComponentMediator();
                 case PowerComponent.WIFI:
                     return new WifiPowerComponentMediator();
                 case PowerComponent.CELLULAR:
-                    return new CellularPowerComponentMediator();
+                    return null;
                 case PowerComponent.ETHERNET:
-                    return new EthernetPowerComponentMediator();
+                    return null;
                 case PowerComponent.PROJECTION:
-                    return new ProjectionPowerComponentMediator();
+                    return null;
                 case PowerComponent.NFC:
-                    return new NfcPowerComponentMediator();
+                    return null;
                 case PowerComponent.INPUT:
-                    return new InputPowerComponentMediator();
+                    return null;
                 case PowerComponent.VOICE_INTERACTION:
                     return new VoiceInteractionPowerComponentMediator();
                 case PowerComponent.VISUAL_INTERACTION:
-                    return new VisualInteractionPowerComponentMediator();
+                    return null;
                 case PowerComponent.TRUSTED_DEVICE_DETECTION:
-                    return new TrustedDeviceDetectionPowerComponentMediator();
+                    return null;
                 case PowerComponent.MICROPHONE:
-                    return new MicroPhonePowerComponentMediator();
+                    // We don't control microphone in framework level, because microphone is enabled
+                    // or disabled in audio HAL according to the current power policy.
+                    return null;
                 case PowerComponent.BLUETOOTH:
                     // com.android.car.BluetoothDeviceConnectionPolicy handles power state change.
                     // So, bluetooth mediator is not created.
@@ -359,6 +346,7 @@
                     // GNSS HAL handles power state change. So, location mediator is not created.
                     return null;
                 default:
+                    Slog.w(TAG, "Unknown component(" + component + ")");
                     return null;
             }
         }
diff --git a/service/src/com/android/car/systeminterface/DisplayInterface.java b/service/src/com/android/car/systeminterface/DisplayInterface.java
index 3bd90c8..30bb304 100644
--- a/service/src/com/android/car/systeminterface/DisplayInterface.java
+++ b/service/src/com/android/car/systeminterface/DisplayInterface.java
@@ -50,14 +50,39 @@
  */
 public interface DisplayInterface {
     /**
+     * Sets display brightness.
+     *
      * @param brightness Level from 0 to 100%
      */
     void setDisplayBrightness(int brightness);
+
+    /**
+     * Turns on or off display.
+     *
+     * @param on {@code true} to turn on, {@code false} to turn off.
+     */
     void setDisplayState(boolean on);
+
+    /**
+     * Starts monitoring the display state change.
+     *
+     * <p> When there is a change, {@link CarPowerManagementService} is notified.
+     *
+     * @param service {@link CarPowerManagementService} to listen to the change.
+     */
     void startDisplayStateMonitoring(CarPowerManagementService service);
+
+    /**
+     * Stops monitoring the display state change.
+     */
     void stopDisplayStateMonitoring();
 
     /**
+     * Gets the current on/off state of display.
+     */
+    boolean isDisplayEnabled();
+
+    /**
      * Refreshing display brightness. Used when user is switching and car turned on.
      */
     void refreshDisplayBrightness();
@@ -244,6 +269,11 @@
             }
         }
 
+        @Override
+        public boolean isDisplayEnabled() {
+            return isMainDisplayOn();
+        }
+
         private void onUsersUpdate() {
             synchronized (mLock) {
                 if (mService == null) {
diff --git a/service/src/com/android/car/systeminterface/SystemInterface.java b/service/src/com/android/car/systeminterface/SystemInterface.java
index 928962d..8096946 100644
--- a/service/src/com/android/car/systeminterface/SystemInterface.java
+++ b/service/src/com/android/car/systeminterface/SystemInterface.java
@@ -147,6 +147,11 @@
     }
 
     @Override
+    public boolean isDisplayEnabled() {
+        return mDisplayInterface.isDisplayEnabled();
+    }
+
+    @Override
     public WearInformationProvider[] getFlashWearInformationProviders() {
         return mStorageMonitoringInterface.getFlashWearInformationProviders();
     }
diff --git a/tests/carservice_test/src/com/android/car/MockedCarTestBase.java b/tests/carservice_test/src/com/android/car/MockedCarTestBase.java
index b444bbb..391a643 100644
--- a/tests/carservice_test/src/com/android/car/MockedCarTestBase.java
+++ b/tests/carservice_test/src/com/android/car/MockedCarTestBase.java
@@ -393,6 +393,11 @@
 
         @Override
         public void refreshDisplayBrightness() {}
+
+        @Override
+        public boolean isDisplayEnabled() {
+            return true;
+        }
     }
 
     static final class MockIOInterface implements IOInterface {
diff --git a/tests/carservice_test/src/com/android/car/power/CarPowerManagementTest.java b/tests/carservice_test/src/com/android/car/power/CarPowerManagementTest.java
index 228695a..bc2bdf8 100644
--- a/tests/carservice_test/src/com/android/car/power/CarPowerManagementTest.java
+++ b/tests/carservice_test/src/com/android/car/power/CarPowerManagementTest.java
@@ -359,6 +359,11 @@
 
         @Override
         public void refreshDisplayBrightness() {}
+
+        @Override
+        public boolean isDisplayEnabled() {
+            return mDisplayOn;
+        }
     }
 
     private class PowerStatePropertyHandler implements VehicleHalPropertyHandler {
diff --git a/tests/carservice_unit_test/src/com/android/car/hardware/power/CarPowerManagerUnitTest.java b/tests/carservice_unit_test/src/com/android/car/hardware/power/CarPowerManagerUnitTest.java
index 0537e8c..4a07873 100644
--- a/tests/carservice_unit_test/src/com/android/car/hardware/power/CarPowerManagerUnitTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/hardware/power/CarPowerManagerUnitTest.java
@@ -53,6 +53,7 @@
 import com.android.car.systeminterface.SystemInterface;
 import com.android.car.systeminterface.SystemStateInterface;
 import com.android.car.user.CarUserService;
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.IVoiceInteractionManagerService;
 
 import org.junit.After;
@@ -407,6 +408,8 @@
     }
 
     private static final class MockDisplayInterface implements DisplayInterface {
+        private final Object mLock = new Object();
+        @GuardedBy("mLock")
         private boolean mDisplayOn = true;
         private final Semaphore mDisplayStateWait = new Semaphore(0);
 
@@ -414,18 +417,18 @@
         public void setDisplayBrightness(int brightness) {}
 
         @Override
-        public synchronized void setDisplayState(boolean on) {
-            mDisplayOn = on;
+        public void setDisplayState(boolean on) {
+            synchronized (mLock) {
+                mDisplayOn = on;
+            }
             mDisplayStateWait.release();
         }
 
-        public synchronized boolean getDisplayState() {
-            return mDisplayOn;
-        }
-
         public boolean waitForDisplayStateChange(long timeoutMs) throws Exception {
             JavaMockitoHelper.await(mDisplayStateWait, timeoutMs);
-            return mDisplayOn;
+            synchronized (mLock) {
+                return mDisplayOn;
+            }
         }
 
         @Override
@@ -436,6 +439,13 @@
 
         @Override
         public void refreshDisplayBrightness() {}
+
+        @Override
+        public boolean isDisplayEnabled() {
+            synchronized (mLock) {
+                return mDisplayOn;
+            }
+        }
     }
 
     /**
diff --git a/tests/carservice_unit_test/src/com/android/car/power/CarPowerManagementServiceUnitTest.java b/tests/carservice_unit_test/src/com/android/car/power/CarPowerManagementServiceUnitTest.java
index dfbd685..3f18b01 100644
--- a/tests/carservice_unit_test/src/com/android/car/power/CarPowerManagementServiceUnitTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/power/CarPowerManagementServiceUnitTest.java
@@ -66,6 +66,7 @@
 import com.android.car.test.utils.TemporaryDirectory;
 import com.android.car.test.utils.TemporaryFile;
 import com.android.car.user.CarUserService;
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.IVoiceInteractionManagerService;
 import com.android.internal.os.IResultReceiver;
 
@@ -481,7 +482,7 @@
         mPowerSignalListener.waitForSleepExit(WAIT_TIMEOUT_MS);
         mService.scheduleNextWakeupTime(WAKE_UP_DELAY);
         // second processing after wakeup
-        assertThat(mDisplayInterface.getDisplayState()).isFalse();
+        assertThat(mDisplayInterface.isDisplayEnabled()).isFalse();
 
         mService.setStateForTesting(/* isBooting= */ false);
 
@@ -507,7 +508,7 @@
         // Since we just woke up from shutdown, wake up time will be 0
         assertStateReceived(PowerHalService.SET_DEEP_SLEEP_EXIT, 0);
         assertVoiceInteractionEnabled();
-        assertThat(mDisplayInterface.getDisplayState()).isFalse();
+        assertThat(mDisplayInterface.isDisplayEnabled()).isFalse();
     }
 
     private void assertStateReceived(int expectedState, int expectedParam) throws Exception {
@@ -579,6 +580,8 @@
     }
 
     private static final class MockDisplayInterface implements DisplayInterface {
+        private final Object mLock = new Object();
+        @GuardedBy("mLock")
         private boolean mDisplayOn = true;
         private final Semaphore mDisplayStateWait = new Semaphore(0);
 
@@ -586,15 +589,13 @@
         public void setDisplayBrightness(int brightness) {}
 
         @Override
-        public synchronized void setDisplayState(boolean on) {
-            mDisplayOn = on;
+        public void setDisplayState(boolean on) {
+            synchronized (mLock) {
+                mDisplayOn = on;
+            }
             mDisplayStateWait.release();
         }
 
-        public synchronized boolean getDisplayState() {
-            return mDisplayOn;
-        }
-
         private void waitForDisplayOn(long timeoutMs) throws Exception {
             waitForDisplayState(true, timeoutMs);
         }
@@ -605,7 +606,12 @@
 
         private void waitForDisplayState(boolean desiredState, long timeoutMs) throws Exception {
             int nTries = 0;
-            while (mDisplayOn != desiredState) {
+            while (true) {
+                synchronized (mLock) {
+                    if (mDisplayOn == desiredState) {
+                        break;
+                    }
+                }
                 if (nTries > 5) throw new IllegalStateException("timeout");
                 waitForSemaphore(mDisplayStateWait, timeoutMs);
                 nTries++;
@@ -620,6 +626,13 @@
 
         @Override
         public void refreshDisplayBrightness() {}
+
+        @Override
+        public boolean isDisplayEnabled() {
+            synchronized (mLock) {
+                return mDisplayOn;
+            }
+        }
     }
 
     private static final class MockSystemStateInterface implements SystemStateInterface {