CEC: Handle <Set Menu Language> message on playback devices

Bug: 16819131, Bug: 21505123
Change-Id: I3dc6439c13c2ebe5591d9e27c0c86419ed440f9b
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index 77b800e..7999321 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -246,6 +246,8 @@
                 return handleRequestActiveSource(message);
             case Constants.MESSAGE_GET_MENU_LANGUAGE:
                 return handleGetMenuLanguage(message);
+            case Constants.MESSAGE_SET_MENU_LANGUAGE:
+                return handleSetMenuLanguage(message);
             case Constants.MESSAGE_GIVE_PHYSICAL_ADDRESS:
                 return handleGivePhysicalAddress();
             case Constants.MESSAGE_GIVE_OSD_NAME:
@@ -377,6 +379,14 @@
     }
 
     @ServiceThreadOnly
+    protected boolean handleSetMenuLanguage(HdmiCecMessage message) {
+        assertRunOnServiceThread();
+        Slog.w(TAG, "Only Playback device can handle <Set Menu Language>:" + message.toString());
+        // 'return false' will cause to reply with <Feature Abort>.
+        return false;
+    }
+
+    @ServiceThreadOnly
     protected boolean handleGiveOsdName(HdmiCecMessage message) {
         assertRunOnServiceThread();
         // Note that since this method is called after logical address allocation is done,
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index 30a9b43..493471b 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -26,9 +26,14 @@
 import android.provider.Settings.Global;
 import android.util.Slog;
 
+import com.android.internal.app.LocalePicker;
+import com.android.internal.app.LocalePicker.LocaleInfo;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
 
+import java.io.UnsupportedEncodingException;
+import java.util.List;
+
 /**
  * Represent a logical device of type Playback residing in Android system.
  */
@@ -306,6 +311,35 @@
         return true;  // Broadcast message.
     }
 
+    @ServiceThreadOnly
+    protected boolean handleSetMenuLanguage(HdmiCecMessage message) {
+        assertRunOnServiceThread();
+
+        try {
+            String iso3Language = new String(message.getParams(), 0, 3, "US-ASCII");
+
+            // Don't use Locale.getAvailableLocales() since it returns a locale
+            // which is not available on Settings.
+            final List<LocaleInfo> localeInfos = LocalePicker.getAllAssetLocales(
+                    mService.getContext(), false);
+            for (LocaleInfo localeInfo : localeInfos) {
+                if (localeInfo.getLocale().getISO3Language().equals(iso3Language)) {
+                    // WARNING: CEC adopts ISO/FDIS-2 for language code, while Android requires
+                    // additional country variant to pinpoint the locale. This keeps the right
+                    // locale from being chosen. 'eng' in the CEC command, for instance,
+                    // will always be mapped to en-AU among other variants like en-US, en-GB,
+                    // an en-IN, which may not be the expected one.
+                    LocalePicker.updateLocale(localeInfo.getLocale());
+                    return true;
+                }
+            }
+            Slog.w(TAG, "Can't handle <Set Menu Language> of " + iso3Language);
+            return false;
+        } catch (UnsupportedEncodingException e) {
+            return false;
+        }
+    }
+
     @Override
     @ServiceThreadOnly
     protected void sendStandby(int deviceId) {