Snap for 10453563 from 4f1d7f1fcc787d6fb54e2255405e30477c0d00c4 to mainline-adservices-release

Change-Id: I457c9fdd209b84f95a2883069ea3b011836a10ca
diff --git a/FrameworkPackageStubs/AndroidManifest.xml b/FrameworkPackageStubs/AndroidManifest.xml
index 2803156..393021b 100644
--- a/FrameworkPackageStubs/AndroidManifest.xml
+++ b/FrameworkPackageStubs/AndroidManifest.xml
@@ -107,6 +107,33 @@
             </intent-filter>
         </activity>
 
+        <!-- Camera package stubs -->
+        <activity android:name=".Stubs$CameraStub"
+            android:exported="true"
+            android:label="@string/stub_name"
+            android:excludeFromRecents="true"
+            android:visibleToInstantApps="true"
+            >
+            <meta-data android:name="com.android.tv.stub" android:value="true" />
+            <intent-filter android:priority="-1">
+                <action android:name="android.media.action.IMAGE_CAPTURE" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+            <intent-filter android:priority="-1">
+                <action android:name="android.media.action.VIDEO_CAPTURE" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+            <intent-filter android:priority="-1">
+                <action android:name="android.media.action.STILL_IMAGE_CAMERA" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+            <intent-filter android:priority="-1">
+                <action android:name="android.media.action.VIDEO_CAMERA" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+
+
         <!-- Contacts package stubs.
 
              Copied all intent filters from all activities
@@ -634,6 +661,10 @@
                 <action android:name="android.settings.REQUEST_MANAGE_MEDIA" />
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
+            <intent-filter android:priority="-1">
+                <action android:name="android.intent.action.SET_WALLPAPER" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
         </activity>
 
         <activity android:name=".Stubs$SettingsPrivacyStub"
diff --git a/FrameworkPackageStubs/res/values-az/strings.xml b/FrameworkPackageStubs/res/values-az/strings.xml
index 889abc9..e811c68 100644
--- a/FrameworkPackageStubs/res/values-az/strings.xml
+++ b/FrameworkPackageStubs/res/values-az/strings.xml
@@ -3,5 +3,5 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="1051886861071228918">"Fəaliyyət Genişlənməsi"</string>
     <string name="message_not_supported" msgid="5269947674108844893">"Bunu edə bilən tətbiqiniz yoxdur"</string>
-    <string name="stub_name" msgid="2907730040872891281">"Heç bir"</string>
+    <string name="stub_name" msgid="2907730040872891281">"Heç biri"</string>
 </resources>
diff --git a/FrameworkPackageStubs/res/values-en-rCA/strings.xml b/FrameworkPackageStubs/res/values-en-rCA/strings.xml
index 0867d5e..8442f5f 100644
--- a/FrameworkPackageStubs/res/values-en-rCA/strings.xml
+++ b/FrameworkPackageStubs/res/values-en-rCA/strings.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="1051886861071228918">"Activity stub"</string>
+    <string name="app_name" msgid="1051886861071228918">"Activity Stub"</string>
     <string name="message_not_supported" msgid="5269947674108844893">"You don\'t have an app that can do this"</string>
     <string name="stub_name" msgid="2907730040872891281">"None"</string>
 </resources>
diff --git a/FrameworkPackageStubs/res/values-ro/strings.xml b/FrameworkPackageStubs/res/values-ro/strings.xml
index ecffa59..963f0e9 100644
--- a/FrameworkPackageStubs/res/values-ro/strings.xml
+++ b/FrameworkPackageStubs/res/values-ro/strings.xml
@@ -2,6 +2,6 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="1051886861071228918">"Activity Stub"</string>
-    <string name="message_not_supported" msgid="5269947674108844893">"Nu aveți o aplicație care poate face această acțiune"</string>
+    <string name="message_not_supported" msgid="5269947674108844893">"Nu ai o aplicație care poate face această acțiune"</string>
     <string name="stub_name" msgid="2907730040872891281">"Nu există"</string>
 </resources>
diff --git a/FrameworkPackageStubs/src/com/android/tv/frameworkpackagestubs/Stubs.java b/FrameworkPackageStubs/src/com/android/tv/frameworkpackagestubs/Stubs.java
index 34523b1..88a5810 100644
--- a/FrameworkPackageStubs/src/com/android/tv/frameworkpackagestubs/Stubs.java
+++ b/FrameworkPackageStubs/src/com/android/tv/frameworkpackagestubs/Stubs.java
@@ -88,6 +88,11 @@
     public static class CalendarStub extends BaseActivity {}
 
     /**
+     * Stub activity for camera events.
+     */
+    public static class CameraStub extends BaseActivity {}
+
+    /**
      * Stub activity for contacts events.
      */
     public static class ContactsStub extends BaseActivity {}
diff --git a/OWNERS b/OWNERS
index d823953..da87638 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,6 +1,16 @@
 # Bug component: 760438
 
-hgchen@google.com
+# OS-wide changes in device/google/atv and Cuttlefish
+aabdagic@google.com
 rgl@google.com
-shaopengjia@google.com
-wyau@google.com
\ No newline at end of file
+robhor@google.com
+shaopengjia@google.com #{LAST_RESORT_SUGGESTION}
+tolstykh@google.com
+
+# TV emulator and boot logo
+alpic@google.com
+kozachuk@google.com
+
+# GSI targets
+hgchen@google.com
+wyau@google.com
diff --git a/TvSampleLeanbackLauncher/Android.bp b/TvSampleLeanbackLauncher/Android.bp
index 8b525f0..d24dc12 100644
--- a/TvSampleLeanbackLauncher/Android.bp
+++ b/TvSampleLeanbackLauncher/Android.bp
@@ -15,14 +15,25 @@
     filename_from_src: true,
 }
 
-android_app_import {
-  name: "TvSampleLeanbackLauncher",
-  apk: "TvSampleLeanbackLauncher.apk",
-  presigned: true,
-  privileged: true,
-  product_specific: true,
-  required: ["privapp_whitelist_com.example.sampleleanbacklauncher"],
-  dex_preopt: {
-    enabled: false,
-  },
+
+android_app {
+    name: "TvSampleLeanbackLauncher",
+    srcs: ["src/main/**/*.java"],
+    resource_dirs: ["src/main/res"],
+    privileged: true,
+    product_specific: true,
+    sdk_version: "current",
+    target_sdk_version: "current",
+    required: ["privapp_whitelist_com.example.sampleleanbacklauncher"],
+    static_libs: [
+        "androidx.annotation_annotation",
+        "androidx.appcompat_appcompat",
+        "androidx.recyclerview_recyclerview",
+        "androidx.leanback_leanback",
+        "androidx.leanback_leanback-preference",
+        "androidx.preference_preference",
+        "androidx.legacy_legacy-preference-v14",
+        "androidx.localbroadcastmanager_localbroadcastmanager",
+    ],
+    manifest: "src/main/AndroidManifest.xml",
 }
diff --git a/TvSampleLeanbackLauncher/TvSampleLeanbackLauncher.apk b/TvSampleLeanbackLauncher/TvSampleLeanbackLauncher.apk
deleted file mode 100644
index 6afbf52..0000000
--- a/TvSampleLeanbackLauncher/TvSampleLeanbackLauncher.apk
+++ /dev/null
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/androidTest/java/com/example/sampleleanbacklauncher/apps/LaunchItemTest.java b/TvSampleLeanbackLauncher/src/androidTest/java/com/example/sampleleanbacklauncher/apps/LaunchItemTest.java
new file mode 100644
index 0000000..2acb53f
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/androidTest/java/com/example/sampleleanbacklauncher/apps/LaunchItemTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2016 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.example.sampleleanbacklauncher.apps;
+
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.graphics.drawable.Drawable;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+@RunWith(AndroidJUnit4.class)
+public class LaunchItemTest {
+
+    private LaunchItem createTestLaunchItem(String packageName, String name, Drawable banner,
+            Drawable icon, CharSequence label, long priority) {
+        final ActivityInfo activityInfo = mock(ActivityInfo.class);
+        when(activityInfo.loadBanner(any(PackageManager.class))).thenReturn(banner);
+        activityInfo.packageName = packageName;
+        activityInfo.name = name;
+
+        final ResolveInfo resolveInfo = mock(ResolveInfo.class);
+        when(resolveInfo.loadIcon(any(PackageManager.class))).thenReturn(icon);
+        when(resolveInfo.loadLabel(any(PackageManager.class))).thenReturn(label);
+        resolveInfo.activityInfo = activityInfo;
+
+        final Context context = mock(Context.class);
+        when(context.getPackageManager()).thenReturn(null);
+
+        return new LaunchItem(context, resolveInfo, priority);
+    }
+
+    @Test
+    public void testGetIntent() throws Exception {
+        final LaunchItem item =
+                createTestLaunchItem("com.example.testPackage", "com.example.testName",
+                        null, null, null, 0);
+        assertNotNull(item.getIntent());
+        assertNotNull(item.getIntent().getComponent());
+        assertEquals("com.example.testPackage", item.getIntent().getComponent().getPackageName());
+        assertEquals("com.example.testName", item.getIntent().getComponent().getClassName());
+    }
+
+    @Test
+    public void testCompareTo() throws Exception {
+        final LaunchItem item = createTestLaunchItem("", "", null, null, "A label", 0);
+        final LaunchItem priorityItem = createTestLaunchItem("", "", null, null, "Z label", 1);
+        final LaunchItem alphaItem = createTestLaunchItem("", "", null, null, "Z label", 0);
+
+        assertTrue("priorityItem should sort before item", item.compareTo(priorityItem) > 0);
+        assertTrue("alphaItem should sort after item", item.compareTo(alphaItem) < 0);
+        assertTrue("item should sort equal to item", item.compareTo(item) == 0);
+    }
+}
diff --git a/TvSampleLeanbackLauncher/src/main/AndroidManifest.xml b/TvSampleLeanbackLauncher/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..070967b
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/AndroidManifest.xml
@@ -0,0 +1,57 @@
+<!--
+  ~ Copyright (C) 2023 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
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.sampleleanbacklauncher">
+
+    <!-- ACCESS_WIFI_STATE and ACCESS_NETWORK_STATE are used for updating the network settings entry icon -->
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.ACCESS_NOTIFICATIONS" />
+
+    <uses-feature android:name="android.software.leanback" android:required="true" />
+
+    <application
+        android:allowBackup="true"
+        android:icon="@mipmap/ic_launcher"
+        android:label="@string/app_name"
+        android:supportsRtl="true"
+        android:theme="@style/AppTheme">
+        <activity android:name=".LauncherActivity"
+            android:launchMode="singleTask"
+            android:resumeWhilePausing="true"
+            android:exported="true">
+            <!-- Need to be priority 2 to avoid conflict with SetupWraith (= 3) -->
+            <intent-filter android:priority="2">
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.intent.category.HOME" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+
+        </activity>
+        <activity
+            android:name=".notifications.NotificationsSidePanelActivity"
+            android:theme="@style/NotificationsSidePanel"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="com.android.tv.NOTIFICATIONS_PANEL"/>
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+        <service android:name=".apps.LaunchItemsManager" android:exported="false" />
+    </application>
+</manifest>
diff --git a/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/LauncherActivity.java b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/LauncherActivity.java
new file mode 100644
index 0000000..b14ac59
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/LauncherActivity.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2016 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.example.sampleleanbacklauncher;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+
+import com.example.sampleleanbacklauncher.apps.AppFragment;
+import com.example.sampleleanbacklauncher.search.SearchFragment;
+
+public class LauncherActivity extends Activity {
+    private SystemUpdateChecker mSystemUpdateChecker;
+    private static final int SYSTEM_UPDATE_CHECKER_REQUEST = 0;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.launcher);
+
+        if (savedInstanceState == null) {
+            getFragmentManager().beginTransaction()
+                    // Search row
+                    .add(R.id.launcher_container,
+                            SearchFragment.newInstance())
+                    // Apps row
+                    .add(R.id.launcher_container,
+                            AppFragment.newInstance(AppFragment.ROW_TYPE_APPS))
+                    // Games row
+                    .add(R.id.launcher_container,
+                            AppFragment.newInstance(AppFragment.ROW_TYPE_GAMES))
+                    // Settings row
+                    .add(R.id.launcher_container,
+                            AppFragment.newInstance(AppFragment.ROW_TYPE_SETTINGS))
+                    .commit();
+        }
+
+        mSystemUpdateChecker = new SystemUpdateChecker(this);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        findViewById(R.id.launcher_container).animate().alpha(1f);
+
+        Intent intent = mSystemUpdateChecker.getSystemUpdateCheckerIntent();
+        if (intent != null) {
+            startActivityForResult(intent, SYSTEM_UPDATE_CHECKER_REQUEST);
+        }
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        findViewById(R.id.launcher_container).animate().alpha(0f);
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+        if (requestCode == SYSTEM_UPDATE_CHECKER_REQUEST && resultCode == RESULT_OK) {
+            mSystemUpdateChecker.onSystemUpdateCheckerComplete();
+        }
+    }
+}
diff --git a/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/LauncherConstants.java b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/LauncherConstants.java
new file mode 100644
index 0000000..0ddab37
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/LauncherConstants.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2018 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.example.sampleleanbacklauncher;
+
+public final class LauncherConstants {
+    /**
+     * Intent category for launching TV Settings. This was copied from the system API for sample
+     * purposes. If building using a system SDK, use Intent.CATEGORY_LEANBACK_SETTINGS instead.
+     */
+    public static final String CATEGORY_LEANBACK_SETTINGS =
+            "android.intent.category.LEANBACK_SETTINGS";
+
+    public static final String TVRECOMMENDATIONS_PACKAGE_NAME =
+            "com.google.android.tvrecommendations";
+}
diff --git a/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/SystemUpdateChecker.java b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/SystemUpdateChecker.java
new file mode 100644
index 0000000..2a65845
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/SystemUpdateChecker.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2016 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.example.sampleleanbacklauncher;
+
+import android.annotation.TargetApi;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.preference.PreferenceManager;
+import android.provider.Settings;
+
+import java.util.List;
+
+/**
+ * This is an optional component whose job is to ensure that the system is up-to-date
+ * at boot time.  It delegates this operation to a separate application on the device.
+ */
+@TargetApi(24)
+public class SystemUpdateChecker {
+
+    /**
+     * The application that handles the system update check must be a system application that
+     * has an activity found by this action.
+     */
+    private static final String ACTION_TV_BOOT_COMPLETED =
+            "android.intent.action.TV_BOOT_COMPLETED";
+
+    private static final String PREF_SYSTEM_CHECKED_BOOT_COUNT = "system.checked.boot.count";
+
+    private final Context mContext;
+
+    public SystemUpdateChecker(Context context) {
+        mContext = context;
+    }
+
+    Intent getSystemUpdateCheckerIntent() {
+        // See if we have already performed a system update check since the latest boot
+        // of the device
+        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
+        int checkedBootCount = prefs.getInt(PREF_SYSTEM_CHECKED_BOOT_COUNT, -1);
+        if (checkedBootCount >= getBootCount()) {
+            return null;
+        }
+
+        PackageManager pm = mContext.getPackageManager();
+        List<ResolveInfo> infoList = pm.queryIntentActivities(
+                new Intent(ACTION_TV_BOOT_COMPLETED), 0);
+        if (infoList != null) {
+            for (ResolveInfo resolveInfo : infoList) {
+                if (isSystemApp(resolveInfo)) {
+                    Intent intent = new Intent();
+                    intent.setComponent(new ComponentName(resolveInfo.activityInfo.packageName,
+                            resolveInfo.activityInfo.name));
+                    return intent;
+                }
+            }
+        }
+
+        onSystemUpdateCheckerComplete();
+        return null;
+    }
+
+    void onSystemUpdateCheckerComplete() {
+        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
+        prefs.edit().putInt(PREF_SYSTEM_CHECKED_BOOT_COUNT, getBootCount()).apply();
+    }
+
+    private int getBootCount() {
+        // This API is available on Android N+
+        return Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.BOOT_COUNT, 0);
+    }
+
+    private static boolean isSystemApp(ResolveInfo info) {
+        return (info.activityInfo != null && info.activityInfo.applicationInfo != null &&
+                (info.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0);
+    }
+}
diff --git a/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/apps/AppFragment.java b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/apps/AppFragment.java
new file mode 100644
index 0000000..3795625
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/apps/AppFragment.java
@@ -0,0 +1,450 @@
+/*
+ * Copyright (C) 2016 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.example.sampleleanbacklauncher.apps;
+
+import android.app.Fragment;
+import android.app.LoaderManager;
+import android.content.ActivityNotFoundException;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.CursorLoader;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.Loader;
+import android.content.ServiceConnection;
+import android.database.Cursor;
+import android.database.DatabaseUtils;
+import android.os.Bundle;
+import android.os.IBinder;
+import androidx.annotation.IdRes;
+import androidx.annotation.MainThread;
+import androidx.annotation.Nullable;
+import androidx.annotation.StringDef;
+import androidx.localbroadcastmanager.content.LocalBroadcastManager;
+import androidx.collection.ArrayMap;
+import androidx.recyclerview.widget.SortedList;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.SortedListAdapterCallback;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.Toast;
+import android.widget.TextView;
+
+import com.example.sampleleanbacklauncher.R;
+import com.example.sampleleanbacklauncher.notifications.NotificationsContract;
+import com.example.sampleleanbacklauncher.util.LauncherAsyncTaskLoader;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+
+public class AppFragment extends Fragment {
+    @Retention(RetentionPolicy.SOURCE)
+    @StringDef({ROW_TYPE_APPS, ROW_TYPE_GAMES, ROW_TYPE_SETTINGS})
+    public @interface RowType {}
+    public static final String ROW_TYPE_APPS = "apps";
+    public static final String ROW_TYPE_GAMES = "games";
+    public static final String ROW_TYPE_SETTINGS = "settings";
+
+    private static final String TAG = "AppFragment";
+    private static final boolean DEBUG = false;
+
+    private static final String ARG_ROW_TYPE = "AppFragment.ROW_TYPE";
+
+    private static final int ITEM_LOADER_ID = 1;
+    private static final int NOTIFS_COUNT_LOADER_ID = 2;
+
+    @RowType
+    private String mRowType;
+
+    private AppAdapter mAdapter;
+
+    private LaunchItemsManager mLaunchItemsManager;
+    private final ServiceConnection mLaunchItemsServiceConnection = new ServiceConnection() {
+        @Override
+        public void onServiceConnected(ComponentName componentName, IBinder service) {
+            mLaunchItemsManager =
+                    ((LaunchItemsManager.LocalBinder) service).getLaunchItemsManager();
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName componentName) {
+            mLaunchItemsManager = null;
+        }
+    };
+
+    public static AppFragment newInstance(@RowType String rowType) {
+        Bundle args = new Bundle(1);
+        args.putString(ARG_ROW_TYPE, rowType);
+
+        AppFragment fragment = new AppFragment();
+        fragment.setArguments(args);
+        return fragment;
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        //noinspection WrongConstant
+        mRowType = getArguments().getString(ARG_ROW_TYPE, ROW_TYPE_APPS);
+        final Context context = getContext();
+        context.bindService(new Intent(context, LaunchItemsManager.class),
+                mLaunchItemsServiceConnection, Context.BIND_AUTO_CREATE);
+    }
+
+    @Nullable
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                             Bundle savedInstanceState) {
+        final View root = inflater.inflate(R.layout.app_launch_row, container, false);
+        // Since there's multiple instances of this root view in the parent, they need a unique
+        // id so that view state save/restore works correctly.
+        root.setId(getRootViewId());
+        final TextView rowTitle = (TextView) root.findViewById(R.id.row_title);
+        rowTitle.setText(getRowTitle());
+        final RecyclerView list = (RecyclerView) root.findViewById(android.R.id.list);
+        mAdapter = new AppAdapter();
+        getLoaderManager().initLoader(ITEM_LOADER_ID, null, new ItemLoaderCallbacks());
+        list.setAdapter(mAdapter);
+
+        if (mRowType == ROW_TYPE_SETTINGS) {
+            getLoaderManager().initLoader(NOTIFS_COUNT_LOADER_ID, null,
+                    new NotifsCountLoaderCallbacks());
+        }
+
+        return root;
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        Context context = getContext();
+        context.unbindService(mLaunchItemsServiceConnection);
+    }
+
+    private @IdRes int getRootViewId() {
+        switch (mRowType) {
+            case ROW_TYPE_APPS:
+                return R.id.apps_row;
+            case ROW_TYPE_GAMES:
+                return R.id.games_row;
+            case ROW_TYPE_SETTINGS:
+                return R.id.settings_row;
+        }
+        throw new IllegalStateException("Unknown row type");
+    }
+
+    public CharSequence getRowTitle() {
+        switch (mRowType) {
+            case ROW_TYPE_APPS:
+                return getText(R.string.apps_row_title);
+            case ROW_TYPE_GAMES:
+                return getText(R.string.games_row_title);
+            case ROW_TYPE_SETTINGS:
+                return getText(R.string.settings_row_title);
+        }
+        throw new IllegalStateException("Unknown row type");
+    }
+
+    @MainThread
+    private void updateList(Set<LaunchItem> newItems) {
+        if (!isAdded() || mAdapter == null) {
+            return;
+        }
+
+        final SortedList<LaunchItem> items = mAdapter.getLaunchItems();
+
+        if (newItems == null) {
+            items.clear();
+            return;
+        }
+
+        items.beginBatchedUpdates();
+        try {
+            for (int i = 0; i < items.size(); ) {
+                final LaunchItem item = items.get(i);
+                if (newItems.contains(item)) {
+                    i++;
+                } else {
+                    items.remove(item);
+                }
+            }
+            items.addAll(newItems);
+        } finally {
+            items.endBatchedUpdates();
+        }
+    }
+
+    void onItemClicked(int adapterPosition) {
+        SortedList<LaunchItem> launchItems = mAdapter.getLaunchItems();
+        if (adapterPosition >= launchItems.size()) {
+            Log.e(TAG, "Item clicked out of bounds, index " + adapterPosition +
+                    " size " + launchItems.size());
+            return;
+        }
+        final LaunchItem item = launchItems.get(adapterPosition);
+        try {
+            startActivity(item.getIntent());
+            if (mLaunchItemsManager != null) {
+                mLaunchItemsManager.notifyItemLaunched(item);
+            }
+        } catch (ActivityNotFoundException e) {
+            Log.e(TAG, "Exception launching intent " + item.getIntent(), e);
+            Toast.makeText(getContext(), getString(R.string.app_unavailable),
+                    Toast.LENGTH_SHORT).show();
+        }
+    }
+
+    public class AppViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
+        private final View mView;
+
+        public AppViewHolder(View v) {
+            super(v);
+            mView = v;
+        }
+
+        public void bind(LaunchItem launchItem) {
+            mView.findViewById(R.id.frame).setContentDescription(launchItem.getLabel());
+            mView.findViewById(R.id.frame).setOnClickListener(this);
+
+            int bannerVisibility = launchItem.getBanner() == null ? View.GONE: View.VISIBLE;
+            mView.findViewById(R.id.banner).setVisibility(bannerVisibility);
+
+            int llVisibility = launchItem.getBanner() == null ? View.GONE: View.VISIBLE;
+            mView.findViewById(R.id.ll).setVisibility(llVisibility);
+
+            TextView launchItemLabel = mView.findViewById(R.id.label);
+            launchItemLabel.setText(launchItem.getLabel());
+
+            ImageView launchItemIcon = mView.findViewById(R.id.icon);
+            launchItemIcon.setImageDrawable(launchItem.getIcon());
+        }
+
+        @Override
+        public void onClick(View v) {
+            final int position = getAdapterPosition();
+            if (position != RecyclerView.NO_POSITION) {
+                onItemClicked(position);
+            }
+        }
+    }
+
+    public class AppAdapter extends RecyclerView.Adapter<AppViewHolder> {
+        private final SortedList<LaunchItem> mLaunchItems;
+        private final Map<ComponentName, Long> mItemIdMap = new ArrayMap<>();
+        private long mNextItemId = 0;
+
+        public AppAdapter() {
+            mLaunchItems = new SortedList<>(LaunchItem.class,
+                    new SortedListAdapterCallback<LaunchItem>(this) {
+                        @Override
+                        public int compare(LaunchItem o1, LaunchItem o2) {
+                            return o1.compareTo(o2);
+                        }
+
+                        @Override
+                        public boolean areContentsTheSame(LaunchItem oldItem, LaunchItem newItem) {
+                            return oldItem.areContentsTheSame(newItem);
+                        }
+
+                        @Override
+                        public boolean areItemsTheSame(LaunchItem item1, LaunchItem item2) {
+                            return Objects.equals(item1, item2);
+                        }
+                    });
+            setHasStableIds(true);
+        }
+
+        @Override
+        public AppViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+            final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
+            return new AppViewHolder(inflater.inflate(R.layout.launch_item, parent, false));
+        }
+
+        @Override
+        public void onBindViewHolder(AppViewHolder holder, int position) {
+            holder.bind(mLaunchItems.get(position));
+        }
+
+        @Override
+        public int getItemCount() {
+            return mLaunchItems.size();
+        }
+
+        public SortedList<LaunchItem> getLaunchItems() {
+            return mLaunchItems;
+        }
+
+        @Override
+        public long getItemId(int position) {
+            ComponentName componentName = mLaunchItems.get(position).getIntent().getComponent();
+            Long id = mItemIdMap.get(componentName);
+            if (id != null) {
+                return id;
+            } else {
+                long newId = mNextItemId++;
+                mItemIdMap.put(componentName, newId);
+                return newId;
+            }
+        }
+    }
+
+    private class ItemLoaderCallbacks implements LoaderManager.LoaderCallbacks<Set<LaunchItem>> {
+        @Override
+        public Loader<Set<LaunchItem>> onCreateLoader(int id, Bundle args) {
+            return new ItemLoader(getContext(), mRowType);
+        }
+
+        @Override
+        public void onLoadFinished(Loader<Set<LaunchItem>> loader, Set<LaunchItem> data) {
+            updateList(data);
+        }
+
+        @Override
+        public void onLoaderReset(Loader<Set<LaunchItem>> loader) {}
+    }
+
+    private static class ItemLoader extends LauncherAsyncTaskLoader<Set<LaunchItem>> {
+
+        private final String mRowType;
+
+        private LaunchItemsManager mLaunchItemsManager;
+        private ServiceConnection mConnection;
+
+        private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                onContentChanged();
+            }
+        };
+
+        public ItemLoader(Context context, String rowType) {
+            super(context);
+            mRowType = rowType;
+        }
+
+        @Override
+        protected void onStartLoading() {
+            super.onStartLoading();
+            if (mConnection == null) {
+                mConnection = new ServiceConnection() {
+                    @Override
+                    public void onServiceConnected(ComponentName name, IBinder service) {
+                        mLaunchItemsManager =
+                                ((LaunchItemsManager.LocalBinder) service).getLaunchItemsManager();
+                        startListening();
+                    }
+
+                    @Override
+                    public void onServiceDisconnected(ComponentName name) {
+                        stopListening();
+                        mLaunchItemsManager = null;
+                    }
+                };
+                getContext().bindService(new Intent(getContext(), LaunchItemsManager.class),
+                        mConnection, Context.BIND_AUTO_CREATE);
+            }
+        }
+
+        private void startListening() {
+            switch (mRowType) {
+                case ROW_TYPE_APPS:
+                    LocalBroadcastManager.getInstance(getContext()).registerReceiver(mReceiver,
+                            new IntentFilter(LaunchItemsManager.ACTION_APP_LIST_INVALIDATED));
+                    break;
+                case ROW_TYPE_GAMES:
+                    LocalBroadcastManager.getInstance(getContext()).registerReceiver(mReceiver,
+                            new IntentFilter(LaunchItemsManager.ACTION_GAME_LIST_INVALIDATED));
+                    break;
+                case ROW_TYPE_SETTINGS:
+                    LocalBroadcastManager.getInstance(getContext()).registerReceiver(mReceiver,
+                            new IntentFilter(LaunchItemsManager.ACTION_SETTINGS_LIST_INVALIDATED));
+                    break;
+            }
+            // Force a load to pick up any changes since we stopped listening
+            onContentChanged();
+        }
+
+        private void stopListening() {
+            LocalBroadcastManager.getInstance(getContext())
+                    .unregisterReceiver(mReceiver);
+        }
+
+        @Override
+        protected void onReset() {
+            super.onReset();
+            if (mConnection != null) {
+                getContext().unbindService(mConnection);
+                mConnection = null;
+            }
+        }
+
+        @Override
+        public Set<LaunchItem> loadInBackground() {
+            if (mLaunchItemsManager != null) {
+                switch (mRowType) {
+                    case ROW_TYPE_APPS:
+                        return mLaunchItemsManager.getAppItems();
+                    case ROW_TYPE_GAMES:
+                        return mLaunchItemsManager.getGameItems();
+                    case ROW_TYPE_SETTINGS:
+                        return mLaunchItemsManager.getSettingsItems();
+                    default:
+                        throw new IllegalStateException("Unknown row type");
+                }
+            } else {
+                return null;
+            }
+        }
+    }
+
+    private class NotifsCountLoaderCallbacks implements LoaderManager.LoaderCallbacks<Cursor> {
+        @Override
+        public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+            return new CursorLoader(getContext(), NotificationsContract.NOTIFS_COUNT_URI,
+                    null, null, null, null);
+        }
+
+        @Override
+        public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+            if (DEBUG) {
+                Log.d(TAG, "onLoadFinished() called with: " + "loader = [" + loader + "], data = ["
+                        + DatabaseUtils.dumpCursorToString(data) + "]");
+            }
+            if (mLaunchItemsManager != null) {
+                mLaunchItemsManager.updateNotifsCountCursor(data);
+            }
+        }
+
+        @Override
+        public void onLoaderReset(Loader<Cursor> loader) {
+            if (DEBUG) {
+                Log.d(TAG, "onLoaderReset() called with: " + "loader = [" + loader + "]");
+            }
+            if (mLaunchItemsManager != null) {
+                mLaunchItemsManager.updateNotifsCountCursor(null);
+            }
+        }
+    }
+}
diff --git a/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/apps/LaunchItem.java b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/apps/LaunchItem.java
new file mode 100644
index 0000000..4f59bde
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/apps/LaunchItem.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2016 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.example.sampleleanbacklauncher.apps;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.graphics.drawable.Drawable;
+import androidx.annotation.NonNull;
+
+import java.util.Objects;
+
+public class LaunchItem implements Comparable<LaunchItem> {
+    protected final Intent mIntent;
+    private final Drawable mBanner;
+    private final Drawable mIcon;
+    private final CharSequence mLabel;
+    private final long mPriority;
+
+    LaunchItem(Context context, Intent intent, Drawable icon, CharSequence label) {
+        mIntent = intent;
+        mIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        final PackageManager packageManager = context.getPackageManager();
+        mBanner = null;
+        mIcon = icon;
+        mLabel = label;
+        mPriority = 0;
+    }
+
+    LaunchItem(Context context, ResolveInfo info, long priority) {
+        mIntent = Intent.makeMainActivity(
+                new ComponentName(info.activityInfo.packageName, info.activityInfo.name));
+        mIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        final PackageManager packageManager = context.getPackageManager();
+        mBanner = info.activityInfo.loadBanner(packageManager);
+        mIcon = info.loadIcon(packageManager);
+        mLabel = info.loadLabel(packageManager);
+        mPriority = priority;
+    }
+
+    public Intent getIntent() {
+        return mIntent;
+    }
+
+    public Drawable getBanner() {
+        return mBanner;
+    }
+
+    public Drawable getIcon() {
+        return mIcon;
+    }
+
+    public CharSequence getLabel() {
+        return mLabel;
+    }
+
+    /**
+     * Returns whether this item should appear identical to the given item.
+     * @param another Item to compare to
+     * @return True if items should appear identical
+     */
+    public boolean areContentsTheSame(LaunchItem another) {
+        return Objects.equals(another.getBanner(), getBanner())
+                && Objects.equals(another.getIcon(), getIcon())
+                && Objects.equals(another.getLabel(), getLabel());
+    }
+
+    @Override
+    public int compareTo(@NonNull LaunchItem another) {
+        long priorityDiff = another.mPriority - mPriority;
+        if (priorityDiff != 0) {
+            return priorityDiff < 0L ? -1 : 1;
+        } else {
+            return getLabel().toString().compareTo(another.getLabel().toString());
+        }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        return o instanceof LaunchItem && Objects.equals(mIntent, ((LaunchItem)o).getIntent());
+    }
+
+    @Override
+    public int hashCode() {
+        return mIntent.hashCode();
+    }
+
+    @Override
+    public String toString() {
+        return mLabel + " -- " + mIntent.getComponent().toString();
+    }
+
+    public String toDebugString() {
+        return "Label: " + mLabel
+                + " Intent: " + mIntent
+                + " Banner: " + mBanner
+                + " Icon: " + mIcon
+                + " Priority: " + Long.toString(mPriority);
+    }
+}
diff --git a/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/apps/LaunchItemsDbHelper.java b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/apps/LaunchItemsDbHelper.java
new file mode 100644
index 0000000..b857bb7
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/apps/LaunchItemsDbHelper.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2016 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.example.sampleleanbacklauncher.apps;
+
+import android.content.ComponentName;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteCursor;
+import android.database.sqlite.SQLiteCursorDriver;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.database.sqlite.SQLiteQuery;
+import android.os.Trace;
+import androidx.annotation.Nullable;
+import androidx.annotation.WorkerThread;
+import android.util.ArrayMap;
+
+import java.util.Date;
+import java.util.Map;
+
+@WorkerThread
+public class LaunchItemsDbHelper extends SQLiteOpenHelper {
+
+    // Increment whenever schema changes
+    private static final int DATABASE_VERSION = 1;
+    // Database name
+    private static final String DATABASE_NAME = "launch_items";
+
+    private static final String CREATE_APP_TABLE =
+            "CREATE TABLE IF NOT EXISTS " + AppDbItem.TABLE_NAME + "("
+                + AppDbItem.COLUMN_COMPONENT + " TEXT NOT NULL PRIMARY KEY , "
+                + AppDbItem.COLUMN_ORDER_PRIORITY + " INTEGER NOT NULL DEFAULT 0 , "
+                + AppDbItem.COLUMN_LAST_OPEN + " INTEGER "
+            + " )";
+
+    public LaunchItemsDbHelper(Context context) {
+        super(context, DATABASE_NAME, new SQLiteDatabase.CursorFactory() {
+            @Override
+            public Cursor newCursor(SQLiteDatabase db, SQLiteCursorDriver masterQuery,
+                                    String editTable, SQLiteQuery query) {
+                return new SQLiteCursor(masterQuery, editTable, query);
+            }
+        }, DATABASE_VERSION);
+    }
+
+    @Override
+    public void onCreate(SQLiteDatabase db) {
+        db.execSQL(CREATE_APP_TABLE);
+    }
+
+    @Override
+    public void onConfigure(SQLiteDatabase db) {
+        super.onConfigure(db);
+        setWriteAheadLoggingEnabled(true);
+    }
+
+    @Override
+    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+        // DO NOT USE CONSTANTS FOR DB UPGRADE STEPS, USE ONLY LITERAL SQL STRINGS!
+    }
+
+    @Override
+    public SQLiteDatabase getWritableDatabase() {
+        Trace.beginSection("getWritableDatabase");
+        try {
+            return super.getWritableDatabase();
+        } finally {
+            Trace.endSection();
+        }
+    }
+
+    @Override
+    public SQLiteDatabase getReadableDatabase() {
+        Trace.beginSection("getReadableDatabase");
+        try {
+            return super.getReadableDatabase();
+        } finally {
+            Trace.endSection();
+        }
+    }
+
+    @Nullable
+    public Map<ComponentName, Date> readLastOpens() {
+        Trace.beginSection("readLastOpens");
+        try {
+            final SQLiteDatabase db = getReadableDatabase();
+
+            try (Cursor c = db.query(
+                    AppDbItem.TABLE_NAME,
+                    new String[] {AppDbItem.COLUMN_COMPONENT, AppDbItem.COLUMN_LAST_OPEN},
+                    null, null, null, null, null, null)) {
+
+                final int componentIndex = c.getColumnIndex(AppDbItem.COLUMN_COMPONENT);
+                final int lastOpenIndex = c.getColumnIndex(AppDbItem.COLUMN_LAST_OPEN);
+
+                final Map<ComponentName, Date> map = new ArrayMap<>(c.getCount());
+                while (c.moveToNext()) {
+                    final ComponentName componentName =
+                            ComponentName.unflattenFromString(c.getString(componentIndex));
+                    if (c.isNull(lastOpenIndex)) {
+                        map.put(componentName, null);
+                    } else {
+                        map.put(componentName, new Date(c.getLong(lastOpenIndex)));
+                    }
+                }
+                return map;
+
+            }
+        } finally {
+            Trace.endSection();
+        }
+    }
+
+    public void writeLastOpen(ComponentName componentName, Date lastOpen) {
+        Trace.beginSection("writeLastOpen");
+        try {
+            final SQLiteDatabase db = getWritableDatabase();
+
+            db.beginTransactionNonExclusive();
+            try {
+                final ContentValues contentValues = new ContentValues(2);
+                contentValues.put(AppDbItem.COLUMN_LAST_OPEN, lastOpen.getTime());
+
+                final int cnt = db.update(
+                        AppDbItem.TABLE_NAME,
+                        contentValues,
+                        AppDbItem.COLUMN_COMPONENT + "=?",
+                        new String[]{componentName.flattenToString()});
+
+                if (cnt < 1) {
+                    contentValues.put(AppDbItem.COLUMN_COMPONENT, componentName.flattenToString());
+                    db.insert(AppDbItem.TABLE_NAME, AppDbItem.COLUMN_COMPONENT, contentValues);
+                }
+                db.setTransactionSuccessful();
+            } finally {
+                db.endTransaction();
+            }
+        } finally {
+            Trace.endSection();
+        }
+    }
+
+    public Map<ComponentName, Long> readOrderPriorities() {
+        Trace.beginSection("readOrderPriorities");
+        try {
+            final SQLiteDatabase db = getReadableDatabase();
+
+            try (Cursor c = db.query(
+                    AppDbItem.TABLE_NAME,
+                    new String[] {AppDbItem.COLUMN_COMPONENT, AppDbItem.COLUMN_ORDER_PRIORITY},
+                    null, null, null, null, null, null)) {
+
+                final int componentIndex = c.getColumnIndex(AppDbItem.COLUMN_COMPONENT);
+                final int priorityIndex = c.getColumnIndex(AppDbItem.COLUMN_ORDER_PRIORITY);
+
+                final Map<ComponentName, Long> map = new ArrayMap<>(c.getCount());
+                while (c.moveToNext()) {
+                    final ComponentName componentName =
+                            ComponentName.unflattenFromString(c.getString(componentIndex));
+                    map.put(componentName, c.getLong(priorityIndex));
+                }
+                return map;
+            }
+        } finally {
+            Trace.endSection();
+        }
+    }
+
+    /**
+     * Writes the order priority field, and does not touch other items' priorities
+     * @param componentName {@link ComponentName} of item to update
+     * @param priority New priority
+     */
+    public void writeOrderPriority(ComponentName componentName, long priority) {
+        Trace.beginSection("writeOrderPriority");
+        try {
+            final SQLiteDatabase db = getWritableDatabase();
+
+            db.beginTransactionNonExclusive();
+            try {
+                final ContentValues contentValues = new ContentValues(2);
+                contentValues.put(AppDbItem.COLUMN_ORDER_PRIORITY, priority);
+
+                final int cnt = db.update(
+                        AppDbItem.TABLE_NAME,
+                        contentValues,
+                        AppDbItem.COLUMN_COMPONENT + "=?",
+                        new String[]{componentName.flattenToString()});
+
+                if (cnt < 1) {
+                    contentValues.put(AppDbItem.COLUMN_COMPONENT, componentName.flattenToString());
+                    db.insert(AppDbItem.TABLE_NAME, AppDbItem.COLUMN_COMPONENT, contentValues);
+                }
+                db.setTransactionSuccessful();
+            } finally {
+                db.endTransaction();
+            }
+        } finally {
+            Trace.endSection();
+        }
+    }
+
+    /**
+     * Writes the order priority field, and increments all priorities equal or greater if the new
+     * priority is greater than 0
+     * @param componentName {@link ComponentName} of item to update
+     * @param priority New priority
+     */
+    public void writeOrderPriorityAndShift(ComponentName componentName, int priority) {
+        Trace.beginSection("writeOrderPriorityAndShift");
+        try {
+            final SQLiteDatabase db = getWritableDatabase();
+
+            db.beginTransactionNonExclusive();
+            try {
+                if (priority > 0) {
+                    db.execSQL("UPDATE " + AppDbItem.TABLE_NAME
+                            + " SET " + AppDbItem.COLUMN_ORDER_PRIORITY + "="
+                                    + AppDbItem.COLUMN_ORDER_PRIORITY + "+1"
+                            + " WHERE " + AppDbItem.COLUMN_ORDER_PRIORITY + ">=?",
+                            new String[] {Integer.toString(priority)});
+                }
+
+                writeOrderPriority(componentName, priority);
+
+                db.setTransactionSuccessful();
+            } finally {
+                db.endTransaction();
+            }
+        } finally {
+            Trace.endSection();
+        }
+    }
+
+    private static class AppDbItem {
+        public static final String TABLE_NAME = "app_items";
+
+        // Primary key
+        public static final String COLUMN_COMPONENT = "component";
+        // Ordering priority: higher numbers are higher in list
+        public static final String COLUMN_ORDER_PRIORITY = "order_priority";
+        // Recency date
+        public static final String COLUMN_LAST_OPEN = "last_open";
+    }
+}
diff --git a/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/apps/LaunchItemsManager.java b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/apps/LaunchItemsManager.java
new file mode 100644
index 0000000..fd68abb
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/apps/LaunchItemsManager.java
@@ -0,0 +1,496 @@
+/*
+ * Copyright (C) 2016 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.example.sampleleanbacklauncher.apps;
+
+import android.app.Service;
+import android.content.BroadcastReceiver;
+import android.content.ComponentCallbacks2;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Configuration;
+import android.database.Cursor;
+import android.net.ConnectivityManager;
+import android.net.Uri;
+import android.net.wifi.WifiManager;
+import android.os.AsyncTask;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.Trace;
+import android.provider.Settings;
+import androidx.annotation.Nullable;
+import androidx.annotation.WorkerThread;
+import androidx.localbroadcastmanager.content.LocalBroadcastManager;
+import android.telephony.PhoneStateListener;
+import android.telephony.SignalStrength;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+
+import com.example.sampleleanbacklauncher.R;
+import com.example.sampleleanbacklauncher.LauncherConstants;
+import com.example.sampleleanbacklauncher.notifications.NotificationsContract;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class LaunchItemsManager extends Service implements ComponentCallbacks2 {
+    private static final String TAG = "LaunchItemsManager";
+
+    public static final String ACTION_APP_LIST_INVALIDATED =
+            "LaunchItemsManager.APP_LIST_INVALIDATED";
+    public static final String ACTION_GAME_LIST_INVALIDATED =
+            "LaunchItemsManager.GAME_LIST_INVALIDATED";
+    public static final String ACTION_SETTINGS_LIST_INVALIDATED =
+            "LaunchItemsManager.SETTINGS_LIST_INVALIDATED";
+
+    private LaunchItemsDbHelper mDbHelper;
+    private final Object mDbHelperLock = new Object();
+
+    private Set<LaunchItem> mAppItems = new ArraySet<>();
+    private Set<LaunchItem> mGameItems = new ArraySet<>();
+    private Set<LaunchItem> mSettingsItems = new ArraySet<>();
+
+    private Map<ComponentName, Date> mLastOpenMap;
+    private Map<ComponentName, Long> mPriorityMap;
+    private Map<String, Long> mOobPriority;
+
+    private volatile boolean mAppListValid;
+    private volatile boolean mGameListValid;
+    private volatile boolean mSettingsListValid;
+
+    private NotificationsLaunchItem mNotifsLaunchItem;
+    private Cursor mNotifsCountCursor = null;
+
+    private final PackageListener mPackageListener = new PackageListener();
+    private final NetworkListener mNetworkListener = new NetworkListener();
+    private final IBinder mLocalBinder = new LocalBinder();
+
+    // We can't query the signal strength directly, so we have to just listen for it all the time.
+    private SignalStrength mSignalStrength;
+    private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
+        @Override
+        public void onSignalStrengthsChanged(SignalStrength signalStrength) {
+            mSignalStrength = signalStrength;
+            invalidateSettingsList();
+        }
+    };
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+
+        IntentFilter packageIntentFilter = new IntentFilter();
+        packageIntentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+        packageIntentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+        packageIntentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+        packageIntentFilter.addAction(Intent.ACTION_PACKAGE_REPLACED);
+        packageIntentFilter.addDataScheme("package");
+        registerReceiver(mPackageListener, packageIntentFilter);
+
+        IntentFilter networkIntentFilter = new IntentFilter();
+        networkIntentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+        networkIntentFilter.addAction(WifiManager.RSSI_CHANGED_ACTION);
+        networkIntentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
+        networkIntentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
+        registerReceiver(mNetworkListener, networkIntentFilter);
+
+        registerComponentCallbacks(this);
+
+        getSystemService(TelephonyManager.class).listen(mPhoneStateListener,
+                PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
+
+        invalidateAllLists();
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+
+        unregisterReceiver(mPackageListener);
+        unregisterReceiver(mNetworkListener);
+
+        unregisterComponentCallbacks(this);
+        getSystemService(TelephonyManager.class).listen(mPhoneStateListener,
+                PhoneStateListener.LISTEN_NONE);
+    }
+
+    @Nullable
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mLocalBinder;
+    }
+
+    @WorkerThread
+    private void ensureDatabase() {
+        synchronized (mDbHelperLock) {
+            if (mDbHelper == null) {
+                mDbHelper = new LaunchItemsDbHelper(getApplicationContext());
+                mLastOpenMap = mDbHelper.readLastOpens();
+                mPriorityMap = mDbHelper.readOrderPriorities();
+                final String[] oobOrder = getResources().getStringArray(R.array.oob_order);
+                mOobPriority = new ArrayMap<>(oobOrder.length);
+                for (int i = 0; i < oobOrder.length; i++) {
+                    mOobPriority.put(oobOrder[i], (long) oobOrder.length - i);
+                }
+            }
+        }
+    }
+
+    @WorkerThread
+    private long getPackagePriority(ResolveInfo info) {
+        ensureDatabase();
+
+        final ComponentName cn =
+                new ComponentName(info.activityInfo.packageName, info.activityInfo.name);
+
+        // Since 1970 was quite a while ago, the last open time should be much larger than
+        // the numeric priority
+        Long priority = mPriorityMap.get(cn);
+        final Date lastOpen = mLastOpenMap.get(cn);
+        if (lastOpen != null) {
+            priority = lastOpen.getTime();
+        }
+        if (priority == null) {
+            if (mOobPriority.containsKey(info.activityInfo.packageName)) {
+                priority = mOobPriority.get(info.activityInfo.packageName);
+            } else {
+                priority = 0L;
+            }
+            mDbHelper.writeOrderPriority(cn, priority);
+            mPriorityMap.put(cn, priority);
+        }
+
+        return priority;
+    }
+
+    @WorkerThread
+    private void updateAppList() {
+        Trace.beginSection("updateAppList");
+        try {
+            final PackageManager packageManager = getPackageManager();
+            List<ResolveInfo> infos = packageManager.queryIntentActivities(
+                    new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_LEANBACK_LAUNCHER),
+                    0);
+            final Set<LaunchItem> appItems = new ArraySet<>(infos.size());
+            for (ResolveInfo info : infos) {
+
+                ApplicationInfo appInfo = info.activityInfo.applicationInfo;
+                if ((appInfo.flags & ApplicationInfo.FLAG_IS_GAME) == 0) {
+                    appItems.add(new LaunchItem(this, info, getPackagePriority(info)));
+                }
+            }
+            mAppItems = appItems;
+            mAppListValid = true;
+        } finally {
+            Trace.endSection();
+        }
+    }
+
+    @WorkerThread
+    private void updateGamesList() {
+        Trace.beginSection("updateGamesList");
+        try {
+            final PackageManager packageManager = getPackageManager();
+            List<ResolveInfo> infos = packageManager.queryIntentActivities(
+                    new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_LEANBACK_LAUNCHER),
+                    0);
+            final Set<LaunchItem> gameItems = new ArraySet<>(infos.size());
+            for (ResolveInfo info : infos) {
+                ApplicationInfo appInfo = info.activityInfo.applicationInfo;
+                if ((appInfo.flags & ApplicationInfo.FLAG_IS_GAME) != 0) {
+                    gameItems.add(new LaunchItem(this, info, getPackagePriority(info)));
+                }
+            }
+            mGameItems = gameItems;
+            mGameListValid = true;
+        } finally {
+            Trace.endSection();
+        }
+    }
+
+    @WorkerThread
+    private void updateSettingsList() {
+        Trace.beginSection("updateSettingsList");
+        try {
+            final PackageManager packageManager = getPackageManager();
+            List<ResolveInfo> networkInfos =
+                    packageManager.queryIntentActivities(new Intent(Settings.ACTION_WIFI_SETTINGS)
+                    .addCategory(LauncherConstants.CATEGORY_LEANBACK_SETTINGS), 0);
+            List<ResolveInfo> settingsInfos = packageManager.queryIntentActivities(
+                    new Intent(Intent.ACTION_MAIN)
+                            .addCategory(LauncherConstants.CATEGORY_LEANBACK_SETTINGS),
+                    PackageManager.GET_RESOLVED_FILTER);
+            final Set<LaunchItem> settingsItems = new ArraySet<>(settingsInfos.size());
+            for (ResolveInfo info : settingsInfos) {
+                if (info.activityInfo == null) {
+                    continue;
+                }
+                boolean isNetwork = false;
+                for (ResolveInfo networkInfo : networkInfos) {
+                    if (networkInfo.activityInfo == null) {
+                        continue;
+                    }
+                    if (TextUtils.equals(networkInfo.activityInfo.name,
+                            info.activityInfo.name)
+                        && TextUtils.equals(networkInfo.activityInfo.packageName,
+                            info.activityInfo.packageName)) {
+                        isNetwork = true;
+                        break;
+                    }
+                }
+                int priority = info.priority;
+                if (isNetwork) {
+                    settingsItems.add(new NetworkLaunchItem(this, info, mSignalStrength, priority));
+                } else {
+                    settingsItems.add(new SettingsLaunchItem(this, info, priority));
+                }
+            }
+
+            mNotifsLaunchItem = new NotificationsLaunchItem(this);
+            mNotifsLaunchItem.setNotificationsCount(getNotifsCount());
+            settingsItems.add(mNotifsLaunchItem);
+
+            mSettingsItems = settingsItems;
+            mSettingsListValid = true;
+        } finally {
+            Trace.endSection();
+        }
+    }
+
+    public void updateNotifsCountCursor(Cursor cursor) {
+        mNotifsCountCursor = cursor;
+        invalidateSettingsList();
+    }
+
+    @WorkerThread
+    private int getNotifsCount() {
+        if (mNotifsCountCursor != null && mNotifsCountCursor.moveToFirst()) {
+            mNotifsCountCursor.moveToFirst();
+            int index = mNotifsCountCursor.getColumnIndex(NotificationsContract.COLUMN_COUNT);
+            return mNotifsCountCursor.getInt(index);
+        }
+        return 0;
+    }
+
+    private void invalidateAllLists() {
+        invalidateAppList();
+        invalidateGameList();
+        invalidateSettingsList();
+    }
+
+    private void invalidateAppList() {
+        mAppListValid = false;
+        LocalBroadcastManager.getInstance(this)
+                .sendBroadcast(new Intent(ACTION_APP_LIST_INVALIDATED));
+    }
+
+    private void invalidateGameList() {
+        mGameListValid = false;
+        LocalBroadcastManager.getInstance(this)
+                .sendBroadcast(new Intent(ACTION_GAME_LIST_INVALIDATED));
+    }
+
+    private void invalidateSettingsList() {
+        mSettingsListValid = false;
+        LocalBroadcastManager.getInstance(this)
+                .sendBroadcast(new Intent(ACTION_SETTINGS_LIST_INVALIDATED));
+    }
+
+    @WorkerThread
+    public Set<LaunchItem> getAppItems() {
+        if (!mAppListValid) {
+            updateAppList();
+        }
+        return mAppItems;
+    }
+
+    @WorkerThread
+    public Set<LaunchItem> getGameItems() {
+        if (!mGameListValid) {
+            updateGamesList();
+        }
+        return mGameItems;
+    }
+
+    @WorkerThread
+    public Set<LaunchItem> getSettingsItems() {
+        if (!mSettingsListValid) {
+            updateSettingsList();
+        }
+        return mSettingsItems;
+    }
+
+    private void invalidateListsForPackage(String packageName) {
+        boolean updateApps = false;
+        boolean updateGames = false;
+        boolean updateSettings = false;
+
+        // Check if the package was previously listed in apps, games or settings
+        for (final LaunchItem item : mAppItems) {
+            if (TextUtils.equals(item.getIntent().getComponent().getPackageName(), packageName)) {
+                updateApps = true;
+                break;
+            }
+        }
+        if (!updateApps) {
+            // Can't be both an app and a game at the same time
+            for (final LaunchItem item : mGameItems) {
+                if (TextUtils.equals(item.getIntent().getComponent().getPackageName(),
+                        packageName)) {
+                    updateGames = true;
+                    break;
+                }
+            }
+        }
+        for (final LaunchItem item : mSettingsItems) {
+            if (TextUtils.equals(item.getIntent().getPackage(), packageName)) {
+                updateSettings = true;
+                break;
+            }
+        }
+
+        // Check if the app will be listed in apps, games or settings
+        final List<ResolveInfo> leanbackInfos =
+                getPackageManager().queryIntentActivities(
+                        new Intent(Intent.ACTION_MAIN)
+                                .addCategory(Intent.CATEGORY_LEANBACK_LAUNCHER)
+                                .setPackage(packageName),
+                        0);
+        if (!leanbackInfos.isEmpty()) {
+            ApplicationInfo applicationInfo = leanbackInfos.get(0).activityInfo.applicationInfo;
+            if ((applicationInfo.flags & ApplicationInfo.FLAG_IS_GAME) == 0) {
+                updateApps = true;
+            } else {
+                updateGames = true;
+            }
+        }
+        if (!updateSettings) {
+            final List<ResolveInfo> settingsInfos =
+                    getPackageManager().queryIntentActivities(
+                            new Intent(Intent.ACTION_MAIN)
+                                    .addCategory(LauncherConstants.CATEGORY_LEANBACK_SETTINGS)
+                                    .setPackage(packageName),
+                            0);
+            if (!settingsInfos.isEmpty()) {
+                updateSettings = true;
+            }
+        }
+
+        if (updateApps) {
+            invalidateAppList();
+        }
+        if (updateGames) {
+            invalidateGameList();
+        }
+        if (updateSettings) {
+            invalidateSettingsList();
+        }
+    }
+
+    public void notifyItemLaunched(LaunchItem item) {
+        final Date now = new Date();
+        final ComponentName component = item.getIntent().getComponent();
+        mLastOpenMap.put(component, now);
+        new AsyncTask<Void, Void, Void>() {
+            @Override
+            protected Void doInBackground(Void... params) {
+                ensureDatabase();
+                if (component != null) {
+                    mDbHelper.writeLastOpen(component, now);
+                }
+                return null;
+            }
+        }.execute();
+        if (mAppItems.contains(item)) {
+            invalidateAppList();
+        } else if (mGameItems.contains(item)) {
+            invalidateGameList();
+        }
+        // No recency for settings row
+    }
+
+    @Override
+    public void onTrimMemory(int level) {}
+
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        invalidateAllLists();
+    }
+
+    @Override
+    public void onLowMemory() {}
+
+    @Override
+    protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+        writer.println("App Items");
+        if (mAppItems != null) {
+            for (final LaunchItem item : mAppItems.toArray(new LaunchItem[mAppItems.size()])) {
+                writer.println(item.toDebugString());
+            }
+        } else {
+            writer.println("Null");
+        }
+        writer.println("Game Items");
+        if (mGameItems != null) {
+            for (final LaunchItem item : mGameItems.toArray(new LaunchItem[mGameItems.size()])) {
+                writer.println(item.toDebugString());
+            }
+        } else {
+            writer.println("Null");
+        }
+        writer.println("Settings Items");
+        if (mSettingsItems != null) {
+            for (final LaunchItem item :
+                    mSettingsItems.toArray(new LaunchItem[mSettingsItems.size()])) {
+                writer.println(item.toDebugString());
+            }
+        } else {
+            writer.println("Null");
+        }
+    }
+
+    public class LocalBinder extends Binder {
+        public LaunchItemsManager getLaunchItemsManager() {
+            return LaunchItemsManager.this;
+        }
+    }
+
+    public class PackageListener extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final Uri packageUri = Uri.parse(intent.getDataString());
+            invalidateListsForPackage(packageUri.getSchemeSpecificPart());
+        }
+    }
+
+    public class NetworkListener extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            invalidateSettingsList();
+        }
+    }
+}
diff --git a/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/apps/NetworkLaunchItem.java b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/apps/NetworkLaunchItem.java
new file mode 100644
index 0000000..1d9378e
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/apps/NetworkLaunchItem.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2016 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.example.sampleleanbacklauncher.apps;
+
+import android.content.Context;
+import android.content.pm.ResolveInfo;
+import android.graphics.drawable.Drawable;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import androidx.annotation.WorkerThread;
+import android.telephony.SignalStrength;
+import android.telephony.TelephonyManager;
+
+import com.example.sampleleanbacklauncher.R;
+
+public class NetworkLaunchItem extends SettingsLaunchItem {
+
+    private final Drawable mNetworkStateDrawable;
+    private final CharSequence mNetworkStateLabel;
+
+    @WorkerThread
+    public NetworkLaunchItem(Context context, ResolveInfo info, SignalStrength signalStrength,
+            long priority) {
+        super(context, info, priority);
+        final NetworkInfo networkInfo =
+                context.getSystemService(ConnectivityManager.class).getActiveNetworkInfo();
+        if (networkInfo == null) {
+            mNetworkStateDrawable = context.getDrawable(R.drawable.wifi_not_connected_launcher);
+            mNetworkStateLabel = context.getString(R.string.network_settings_disconnected);
+        } else if (networkInfo.getType() == ConnectivityManager.TYPE_ETHERNET) {
+            mNetworkStateDrawable = getEthernetDrawable(context, networkInfo);
+            mNetworkStateLabel = context.getString(R.string.network_settings_disconnected);
+        } else if (networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
+            final WifiInfo wifiInfo =
+                    context.getSystemService(WifiManager.class).getConnectionInfo();
+            mNetworkStateDrawable = getWifiDrawable(context, networkInfo, wifiInfo);
+            mNetworkStateLabel = removeDoubleQuotes(wifiInfo.getSSID());
+        } else if (networkInfo.getType() == ConnectivityManager.TYPE_MOBILE) {
+            mNetworkStateDrawable = getCellularDrawable(context, networkInfo, signalStrength);
+            mNetworkStateLabel = removeDoubleQuotes(
+                    context.getSystemService(TelephonyManager.class).getNetworkOperatorName());
+        } else {
+            mNetworkStateDrawable = context.getDrawable(R.drawable.wifi_not_connected_launcher);
+            mNetworkStateLabel = context.getString(R.string.network_settings_disconnected);
+        }
+    }
+
+    private static Drawable getEthernetDrawable(Context context, NetworkInfo networkInfo) {
+        if (networkInfo.isConnected()) {
+            return context.getDrawable(R.drawable.ethernet_active_launcher);
+        } else {
+            return context.getDrawable(R.drawable.ethernet_no_internet_launcher);
+        }
+    }
+
+    private static Drawable getWifiDrawable(Context context, NetworkInfo networkInfo,
+            WifiInfo wifiInfo) {
+        final int signal = WifiManager.calculateSignalLevel(wifiInfo.getRssi(), 5);
+        if (networkInfo.isConnected()) {
+            switch (signal) {
+                case 4:
+                    return context.getDrawable(R.drawable.wifi_active_4_launcher);
+                case 3:
+                    return context.getDrawable(R.drawable.wifi_active_3_launcher);
+                case 2:
+                    return context.getDrawable(R.drawable.wifi_active_2_launcher);
+                case 1:
+                    return context.getDrawable(R.drawable.wifi_active_1_launcher);
+                case 0:
+                default:
+                    return context.getDrawable(R.drawable.wifi_active_0_launcher);
+            }
+        } else {
+            return context.getDrawable(R.drawable.wifi_no_internet_launcher);
+        }
+    }
+
+    private static Drawable getCellularDrawable(Context context, NetworkInfo networkInfo,
+            SignalStrength signalStrength) {
+        final TelephonyManager telephonyManager =
+                context.getSystemService(TelephonyManager.class);
+        if (telephonyManager.getSimState() != TelephonyManager.SIM_STATE_READY) {
+            return context.getDrawable(R.drawable.cellular_no_sim_launcher);
+        } else if (signalStrength == null) {
+            return context.getDrawable(R.drawable.cellular_null_launcher);
+        } else {
+            if (networkInfo.isConnected()) {
+                switch (signalStrength.getLevel()) {
+                    case 4:
+                        return context.getDrawable(R.drawable.cellular_4_bar_launcher);
+                    case 3:
+                        return context.getDrawable(R.drawable.cellular_3_bar_launcher);
+                    case 2:
+                        return context.getDrawable(R.drawable.cellular_2_bar_launcher);
+                    case 1:
+                        return context.getDrawable(R.drawable.cellular_1_bar_launcher);
+                    case 0:
+                    default:
+                        return context.getDrawable(R.drawable.cellular_0_bar_launcher);
+                }
+            } else {
+                switch (signalStrength.getLevel()) {
+                    case 4:
+                        return context.getDrawable(R.drawable.cellular_no_internet_4_bar_launcher);
+                    case 3:
+                        return context.getDrawable(R.drawable.cellular_no_internet_3_bar_launcher);
+                    case 2:
+                        return context.getDrawable(R.drawable.cellular_no_internet_2_bar_launcher);
+                    case 1:
+                        return context.getDrawable(R.drawable.cellular_no_internet_1_bar_launcher);
+                    case 0:
+                    default:
+                        return context.getDrawable(R.drawable.cellular_no_internet_0_bar_launcher);
+                }
+            }
+        }
+    }
+
+    @Override
+    public Drawable getIcon() {
+        return mNetworkStateDrawable;
+    }
+
+    @Override
+    public CharSequence getLabel() {
+        return mNetworkStateLabel;
+    }
+
+    private static String removeDoubleQuotes(String string) {
+        if (string == null) {
+            return null;
+        }
+        final int length = string.length();
+        if ((length > 1) && (string.charAt(0) == '"') && (string.charAt(length - 1) == '"')) {
+            return string.substring(1, length - 1);
+        }
+        return string;
+    }
+
+}
diff --git a/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/apps/NotificationsLaunchItem.java b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/apps/NotificationsLaunchItem.java
new file mode 100644
index 0000000..5b3bbf4
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/apps/NotificationsLaunchItem.java
@@ -0,0 +1,46 @@
+package com.example.sampleleanbacklauncher.apps;
+
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.util.Log;
+
+import com.example.sampleleanbacklauncher.R;
+
+public class NotificationsLaunchItem extends LaunchItem {
+    private static final String ACTION_OPEN_NOTIFICATIONS = "com.android.tv.NOTIFICATIONS_PANEL";
+
+    // Change this to use a notification panel activity from a different package.
+    private static final String NOTIFICATIONS_PKG = "com.example.sampleleanbacklauncher";
+
+    private final Context mContext;
+    private int mNotifsCount = 0;
+
+    public NotificationsLaunchItem(Context context) {
+        super(context, new Intent(ACTION_OPEN_NOTIFICATIONS).setPackage(NOTIFICATIONS_PKG),
+                context.getResources().getDrawable(R.drawable.ic_notifications, null),
+                context.getResources().getString(R.string.system_notifications));
+        mContext = context;
+    }
+
+    public void setNotificationsCount(int count) {
+        mNotifsCount = count;
+    }
+
+    @Override
+    public Intent getIntent() {
+        return mIntent;
+    }
+
+    @Override
+    public Drawable getBanner() {
+        // No banner for notifications launch item.
+        return null;
+    }
+
+    @Override
+    public CharSequence getLabel() {
+        return mContext.getResources().getQuantityString(R.plurals.notifications_title,
+                mNotifsCount, mNotifsCount);
+    }
+}
diff --git a/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/apps/SettingsLaunchItem.java b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/apps/SettingsLaunchItem.java
new file mode 100644
index 0000000..419c28d
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/apps/SettingsLaunchItem.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2016 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.example.sampleleanbacklauncher.apps;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+import android.graphics.drawable.Drawable;
+
+public class SettingsLaunchItem extends LaunchItem {
+
+    private final Intent mIntent;
+
+    public SettingsLaunchItem(Context context, ResolveInfo info, long priority) {
+        super(context, info, priority);
+        mIntent = Intent.makeMainActivity(
+                new ComponentName(info.activityInfo.packageName, info.activityInfo.name));
+        mIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+        mIntent.removeCategory(Intent.CATEGORY_LAUNCHER);
+    }
+
+    @Override
+    public Intent getIntent() {
+        return mIntent;
+    }
+
+    @Override
+    public Drawable getBanner() {
+        // Settings items don't use a banner.
+        return null;
+    }
+}
diff --git a/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/notifications/NotificationPanelDismissibleItemView.java b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/notifications/NotificationPanelDismissibleItemView.java
new file mode 100644
index 0000000..55ebb22
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/notifications/NotificationPanelDismissibleItemView.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2018 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.example.sampleleanbacklauncher.notifications;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.TextView;
+
+import com.example.sampleleanbacklauncher.R;
+
+/**
+ * View for a dismissible notification displayed in the notifications side panel.
+ * Handles swapping of background in RTL layouts.
+ * Handles dismiss button focus animation.
+ */
+public class NotificationPanelDismissibleItemView extends NotificationPanelItemView {
+    private View mDismissButton;
+    private TextView mDismissText;
+    private int mViewFocusTranslationX;
+    private int mDismissTranslationX;
+    private boolean mIsRtl;
+
+    public NotificationPanelDismissibleItemView(Context context) {
+        super(context);
+        initializeTranslationValues();
+    }
+
+    public NotificationPanelDismissibleItemView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        initializeTranslationValues();
+    }
+
+    private void initializeTranslationValues() {
+        mIsRtl = (getResources().getConfiguration().getLayoutDirection() == LAYOUT_DIRECTION_RTL);
+
+        mViewFocusTranslationX = getResources().getDimensionPixelSize(
+                R.dimen.notification_panel_item_show_button_translate_x);
+        mDismissTranslationX = getResources().getDimensionPixelSize(
+                R.dimen.notification_panel_item_dismiss_translate_x);
+
+
+        if (mIsRtl) {
+            mViewFocusTranslationX *= -1;
+            mDismissTranslationX *= -1;
+        }
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+
+        // This is to set the focus search to the default behavior, so that on dismissible views
+        // the user can navigate left/right to the dismiss button.
+        mMainContentText.setNextFocusLeftId(NO_ID);
+        mMainContentText.setNextFocusRightId(NO_ID);
+
+        mDismissButton = findViewById(R.id.dismiss_button);
+        mDismissText = findViewById(R.id.dismiss_text);
+
+        if (mIsRtl) {
+            mMainContentText.setBackgroundResource(R.drawable.notification_background_left);
+            mDismissButton.setBackgroundResource(R.drawable.notification_background_right);
+        } else {
+            mMainContentText.setBackgroundResource(R.drawable.notification_background_right);
+            mDismissButton.setBackgroundResource(R.drawable.notification_background_left);
+        }
+
+        final AnimatorSet dismissAnimator = new AnimatorSet();
+        ObjectAnimator containerSlide = ObjectAnimator.ofFloat(mMainContentText, View.TRANSLATION_X,
+                mViewFocusTranslationX, mDismissTranslationX);
+        ObjectAnimator dismissButtonSlide = ObjectAnimator.ofFloat(mDismissButton,
+                View.TRANSLATION_X, mViewFocusTranslationX, mDismissTranslationX);
+
+        dismissAnimator.playTogether(dismissButtonSlide, containerSlide);
+
+        dismissAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                collapseText();
+                mDismissText.setVisibility(GONE);
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mDismissButton.setVisibility(INVISIBLE);
+                mMainContentText.setVisibility(INVISIBLE);
+                setBackgroundColor(getContext().getColor(R.color.notification_selection_color));
+                NotificationsUtils.dismissNotification(getContext(), mNotificationKey);
+            }
+        });
+
+        final AnimatorSet gainFocus = new AnimatorSet();
+        ObjectAnimator containerSlideOut = ObjectAnimator.ofFloat(mMainContentText,
+                View.TRANSLATION_X, 0, mViewFocusTranslationX);
+        ObjectAnimator dismissButtonFocusGain = ObjectAnimator.ofFloat(mDismissButton,
+                View.TRANSLATION_X, 0, mViewFocusTranslationX);
+        gainFocus.playTogether(dismissButtonFocusGain, containerSlideOut);
+
+        final AnimatorSet loseFocus = new AnimatorSet();
+        ObjectAnimator containerSlideIn = ObjectAnimator.ofFloat(mMainContentText,
+                View.TRANSLATION_X, mViewFocusTranslationX, 0);
+        ObjectAnimator dismissButtonFocusLost = ObjectAnimator.ofFloat(mDismissButton,
+                View.TRANSLATION_X, mViewFocusTranslationX, 0);
+        loseFocus.playTogether(dismissButtonFocusLost, containerSlideIn);
+        loseFocus.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mDismissText.setVisibility(GONE);
+            }
+        });
+
+        mDismissButton.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if (mNotificationKey != null) {
+                    dismissAnimator.start();
+                }
+            }
+        });
+        mDismissButton.setOnFocusChangeListener(new OnFocusChangeListener() {
+            @Override
+            public void onFocusChange(View view, boolean focused) {
+                if (focused) {
+                    // Slide the views to the side and show the dismiss text
+                    mDismissText.setVisibility(VISIBLE);
+                    gainFocus.start();
+                } else {
+                    // Slide the views back to their original positions and hide the dismiss text
+                    loseFocus.start();
+                }
+            }
+        });
+    }
+
+    @Override
+    public void setNotification(TvNotification notif) {
+        super.setNotification(notif);
+        mDismissText.setText(notif.getDismissButtonLabel());
+        mDismissButton.setVisibility(VISIBLE);
+    }
+}
+
diff --git a/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/notifications/NotificationPanelItemView.java b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/notifications/NotificationPanelItemView.java
new file mode 100644
index 0000000..92dfe76
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/notifications/NotificationPanelItemView.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2018 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.example.sampleleanbacklauncher.notifications;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.RectF;
+import android.text.Layout;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.example.sampleleanbacklauncher.R;
+
+/**
+ * View for a non-dismissible notification displayed in the notifications side panel.
+ */
+public class NotificationPanelItemView extends LinearLayout
+        implements ViewTreeObserver.OnGlobalFocusChangeListener {
+    private RectF mProgressBounds;
+    private Paint mProgressPaint;
+    private Paint mProgressMaxPaint;
+    private int mProgressStrokeWidth;
+    private int mProgressDiameter;
+    private int mProgressPaddingStart;
+    private int mProgressPaddingTop;
+    private int mProgressColor;
+    private int mProgressMaxColor;
+    private int mProgress;
+    private int mProgressMax;
+    private boolean mIsRtl;
+    private ImageView mIcon;
+    private TextView mTitle;
+    private TextView mText;
+    private TextView mExpandedText;
+    protected View mMainContentText;
+    protected String mNotificationKey;
+    private TvNotification mNotification;
+
+    public NotificationPanelItemView(Context context) {
+        super(context);
+        initializeLayoutValues();
+    }
+
+    public NotificationPanelItemView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        initializeLayoutValues();
+    }
+
+    private void initializeLayoutValues() {
+        Configuration config = getContext().getResources().getConfiguration();
+        mIsRtl = (config.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL);
+
+        Resources res = getResources();
+        mProgressStrokeWidth = res.getDimensionPixelSize(
+                R.dimen.notification_progress_stroke_width);
+        mProgressColor = res.getColor(R.color.notification_progress_stroke_color, null);
+        mProgressMaxColor = res.getColor(R.color.notification_progress_stroke_max_color, null);
+        mProgressDiameter = res.getDimensionPixelSize(R.dimen.notification_progress_circle_size);
+        mProgressPaddingTop = res.getDimensionPixelOffset(
+                R.dimen.notification_progress_circle_padding_top);
+        mProgressPaddingStart = res.getDimensionPixelOffset(
+                R.dimen.notification_progress_circle_padding_start);
+
+        mProgressPaint = new Paint();
+        mProgressPaint.setAntiAlias(true);
+        mProgressPaint.setStyle(Paint.Style.STROKE);
+        mProgressPaint.setColor(mProgressColor);
+        mProgressPaint.setStrokeWidth(mProgressStrokeWidth);
+
+        mProgressMaxPaint = new Paint();
+        mProgressMaxPaint.setAntiAlias(true);
+        mProgressMaxPaint.setStyle(Paint.Style.STROKE);
+        mProgressMaxPaint.setColor(mProgressMaxColor);
+        mProgressMaxPaint.setStrokeWidth(mProgressStrokeWidth);
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        getViewTreeObserver().addOnGlobalFocusChangeListener(this);
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        getViewTreeObserver().removeOnGlobalFocusChangeListener(this);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mIcon = findViewById(R.id.notification_icon);
+        mTitle = findViewById(R.id.notification_title);
+        mText = findViewById(R.id.notification_text);
+        mMainContentText = findViewById(R.id.notification_container);
+        mExpandedText = findViewById(R.id.notification_expanded_text);
+
+        mMainContentText.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                if (mNotificationKey != null) {
+                    NotificationsUtils.openNotification(view.getContext(), mNotificationKey);
+                }
+            }
+        });
+    }
+
+    public void setNotification(TvNotification notif) {
+        mNotification = notif;
+        mNotificationKey = notif.getNotificationKey();
+        mTitle.setText(notif.getTitle());
+        mText.setText(notif.getText());
+        if (!TextUtils.isEmpty(notif.getTitle())) {
+            if (!TextUtils.isEmpty(notif.getText())) {
+                String formatting = getResources().getString(
+                        R.string.notification_content_description_format);
+                mMainContentText.setContentDescription(
+                        String.format(formatting, notif.getTitle(), notif.getText()));
+            } else {
+                mMainContentText.setContentDescription(notif.getTitle());
+            }
+        } else {
+            mMainContentText.setContentDescription(notif.getText());
+        }
+        mExpandedText.setText(notif.getText());
+        mIcon.setImageIcon(notif.getSmallIcon());
+        setProgress(notif.getProgress(), notif.getProgressMax());
+        mMainContentText.setVisibility(VISIBLE);
+    }
+
+    public void setProgress(int progress, int progressMax) {
+        mProgress = progress;
+        mProgressMax = progressMax;
+        if (mProgressMax != 0) {
+            if (mProgressBounds == null) {
+                mProgressBounds = new RectF();
+            }
+            setWillNotDraw(false);
+        } else {
+            mProgressBounds = null;
+            setWillNotDraw(true);
+        }
+        requestLayout();
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        super.onLayout(changed, l, t, r, b);
+        if (mProgressBounds != null) {
+            int left, right;
+            int top = mProgressPaddingTop;
+            int bottom = top + mProgressDiameter;
+            if (mIsRtl) {
+                right = r - mProgressPaddingStart;
+                left = right - mProgressDiameter;
+            } else {
+                left = mProgressPaddingStart;
+                right = left + mProgressDiameter;
+            }
+
+            mProgressBounds.set(left, top, right, bottom);
+        }
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        super.onDraw(canvas);
+
+        if (mProgressMax != 0) {
+            float sweepAngle = 360f * mProgress / mProgressMax;
+            if (mIsRtl) {
+                canvas.drawArc(mProgressBounds, -90, -sweepAngle, false, mProgressPaint);
+                canvas.drawArc(mProgressBounds, -90, 360 - sweepAngle, false,
+                        mProgressMaxPaint);
+            } else {
+                canvas.drawArc(mProgressBounds, -90, sweepAngle, false, mProgressPaint);
+                canvas.drawArc(mProgressBounds, sweepAngle - 90, 360 - sweepAngle, false,
+                        mProgressMaxPaint);
+            }
+        }
+    }
+
+    private boolean isContentTextCutOff() {
+        Layout layout = mText.getLayout();
+        if (layout != null) {
+            int lines = layout.getLineCount();
+            if (lines > 0) {
+                int ellipsisCount = layout.getEllipsisCount(lines - 1);
+                if (ellipsisCount > 0) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    protected void expandText() {
+        mText.setVisibility(GONE);
+        mTitle.setMaxLines(2);
+        mExpandedText.setVisibility(VISIBLE);
+        setBackgroundColor(
+                getResources().getColor(R.color.notification_expanded_text_background));
+    }
+
+    protected void collapseText() {
+        mExpandedText.setVisibility(GONE);
+        mTitle.setMaxLines(1);
+        mText.setVisibility(VISIBLE);
+        setBackgroundColor(Color.TRANSPARENT);
+    }
+
+    @Override
+    public void onGlobalFocusChanged(View oldFocus, View newFocus) {
+        View currentFocus = getFocusedChild();
+        if (currentFocus == null) {
+            collapseText();
+        } else if ((newFocus == currentFocus || newFocus.getParent() == currentFocus)
+                && isContentTextCutOff()) {
+            expandText();
+        }
+    }
+}
diff --git a/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/notifications/NotificationsContract.java b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/notifications/NotificationsContract.java
new file mode 100644
index 0000000..fb7606a
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/notifications/NotificationsContract.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2017 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.example.sampleleanbacklauncher.notifications;
+
+import android.net.Uri;
+
+/**
+ * Constants which represent the "contract" for interacting with TV notifications.
+ */
+
+public final class NotificationsContract {
+    private static final String PATH_NOTIFS = "notifications";
+    private static final String PATH_NOTIFS_COUNT = PATH_NOTIFS + "/count";
+
+    // Content provider for notifications
+    private static final String AUTHORITY =
+            "com.android.tv.notifications.NotificationContentProvider";
+
+    public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/" +
+            PATH_NOTIFS);
+    public static final Uri NOTIFS_COUNT_URI = Uri.parse("content://" + AUTHORITY + "/" +
+            PATH_NOTIFS_COUNT);
+
+    public static final String ACTION_NOTIFICATION_HIDE =
+            "android.tvservice.action.NOTIFICATION_HIDE";
+
+    public static final String ACTION_SHOW_UNSHOWN_NOTIFICATIONS =
+            "android.tvservice.action.SHOW_UNSHOWN_NOTIFICATIONS";
+
+    public static final String ACTION_OPEN_NOTIFICATION_PANEL =
+            "com.android.tv.NOTIFICATIONS_PANEL";
+
+    public static final String NOTIFICATION_KEY = "sbn_key";
+
+    public static final String COLUMN_SBN_KEY = "sbn_key";
+    public static final String COLUMN_PACKAGE_NAME = "package_name";
+    public static final String COLUMN_NOTIF_TITLE = "title";
+    public static final String COLUMN_NOTIF_TEXT = "text";
+    public static final String COLUMN_AUTODISMISS = "is_auto_dismiss";
+    public static final String COLUMN_DISMISSIBLE = "dismissible";
+    public static final String COLUMN_ONGOING = "ongoing";
+    public static final String COLUMN_SMALL_ICON = "small_icon";
+    public static final String COLUMN_CHANNEL = "channel";
+    public static final String COLUMN_PROGRESS = "progress";
+    public static final String COLUMN_PROGRESS_MAX = "progress_max";
+    public static final String COLUMN_NOTIFICATION_HIDDEN = "notification_hidden";
+    public static final String COLUMN_FLAGS = "flags";
+    public static final String COLUMN_HAS_CONTENT_INTENT = "has_content_intent";
+    public static final String COLUMN_BIG_PICTURE = "big_picture";
+    public static final String COLUMN_CONTENT_BUTTON_LABEL = "content_button_label";
+    public static final String COLUMN_DISMISS_BUTTON_LABEL = "dismiss_button_label";
+    public static final String COLUMN_TAG = "tag";
+
+    public static final String COLUMN_COUNT = "count";
+}
diff --git a/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/notifications/NotificationsPanelAdapter.java b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/notifications/NotificationsPanelAdapter.java
new file mode 100644
index 0000000..2e9344a
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/notifications/NotificationsPanelAdapter.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2018 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.example.sampleleanbacklauncher.notifications;
+
+import android.app.NotificationManager;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.DatabaseUtils;
+import androidx.recyclerview.widget.RecyclerView;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.example.sampleleanbacklauncher.R;
+
+/**
+ * Adapter for the {@link RecyclerView} in the notifications side panel which displayed
+ * both dismissible and non-dismissible notifications.
+ */
+public class NotificationsPanelAdapter<VH extends RecyclerView.ViewHolder>
+        extends RecyclerView.Adapter<NotificationsPanelAdapter.NotificationPanelViewHolder> {
+    private static final String TAG = "NotifsPanelAdapter";
+    private static final boolean DEBUG = false;
+
+    private static final int TYPE_DISMISSIBLE = 0;
+    private static final int TYPE_PERSISTENT = 1;
+
+    private Cursor mCursor;
+
+    public NotificationsPanelAdapter(Context context, Cursor cursor) {
+        mCursor = cursor;
+        setHasStableIds(true);
+    }
+
+    public Cursor getCursor() {
+        return mCursor;
+    }
+
+    @Override
+    public int getItemCount() {
+        if (mCursor != null) {
+            return mCursor.getCount();
+        }
+        return 0;
+    }
+
+    @Override
+    public NotificationPanelViewHolder onCreateViewHolder(
+            ViewGroup parent, int viewType) {
+        LayoutInflater inflater = LayoutInflater.from(parent.getContext());
+
+        View trayItem;
+        if (viewType == TYPE_DISMISSIBLE) {
+            trayItem = inflater.inflate(R.layout.notification_panel_item_dismissible,
+                    parent, false);
+        } else {
+            trayItem = inflater.inflate(R.layout.notification_panel_item,
+                    parent, false);
+        }
+
+        return new NotificationPanelViewHolder(trayItem);
+    }
+
+    @Override
+    public void onBindViewHolder(NotificationPanelViewHolder holder,
+                                 int position) {
+        if (!mCursor.moveToPosition(position)) {
+            throw new IllegalStateException("Can't move cursor to position " + position);
+        }
+        onBindViewHolder(holder, mCursor);
+    }
+
+    @Override
+    public int getItemViewType(int position) {
+        if (!mCursor.moveToPosition(position)) {
+            throw new IllegalStateException("Can't move cursor to position " + position);
+        }
+
+        boolean dismissible = mCursor.getInt(TvNotification.COLUMN_INDEX_DISMISSIBLE) != 0;
+        boolean ongoing = mCursor.getInt(TvNotification.COLUMN_INDEX_ONGOING) != 0;
+        if (ongoing || !dismissible) {
+            return TYPE_PERSISTENT;
+        } else {
+            return TYPE_DISMISSIBLE;
+        }
+    }
+
+    @Override
+    public long getItemId(int position) {
+        if (!mCursor.moveToPosition(position)) {
+            Log.wtf(TAG, "Can't move cursor to position " + position);
+            return View.NO_ID;
+        }
+
+        String key = mCursor.getString(TvNotification.COLUMN_INDEX_KEY);
+        return key.hashCode();
+    }
+
+    public void onBindViewHolder(NotificationPanelViewHolder viewHolder, Cursor cursor) {
+        TvNotification notif = TvNotification.fromCursor(cursor);
+        viewHolder.setNotification(notif);
+    }
+
+    public static class NotificationPanelViewHolder extends RecyclerView.ViewHolder {
+        public NotificationPanelViewHolder(View itemView) {
+            super(itemView);
+        }
+
+        public void setNotification(TvNotification notification) {
+            ((NotificationPanelItemView) itemView).setNotification(notification);
+        }
+    }
+
+    /**
+     * Swap in a new Cursor, and close the old Cursor.
+     *
+     * @param newCursor The new cursor to be used.
+     */
+    public void changeCursor(Cursor newCursor) {
+        if (DEBUG) {
+            Log.d(TAG, "changeCursor() called with: " + "newCursor = [" +
+                    DatabaseUtils.dumpCursorToString(newCursor) + "]");
+        }
+
+        mCursor = newCursor;
+        notifyDataSetChanged();
+    }
+}
\ No newline at end of file
diff --git a/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/notifications/NotificationsSidePanelActivity.java b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/notifications/NotificationsSidePanelActivity.java
new file mode 100644
index 0000000..367a348
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/notifications/NotificationsSidePanelActivity.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2018 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.example.sampleleanbacklauncher.notifications;
+
+import android.app.Activity;
+import android.app.LoaderManager;
+import android.content.CursorLoader;
+import android.content.Loader;
+import android.database.Cursor;
+import android.os.Bundle;
+import androidx.annotation.Nullable;
+import androidx.leanback.widget.VerticalGridView;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+import android.transition.Scene;
+import android.transition.Slide;
+import android.transition.Transition;
+import android.transition.TransitionManager;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+
+import com.example.sampleleanbacklauncher.R;
+
+/**
+ * Displays a side panel containing a list of notifications.
+ */
+
+public class NotificationsSidePanelActivity extends Activity
+        implements LoaderManager.LoaderCallbacks<Cursor>{
+    private static final String TAG = "NotifsSidePanel";
+    private NotificationsPanelAdapter mPanelAdapter;
+    private VerticalGridView mNotifsList;
+    private View mNoNotifsMessage;
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        final ViewGroup root = findViewById(android.R.id.content);
+
+        mPanelAdapter = new NotificationsPanelAdapter(
+                NotificationsSidePanelActivity.this,null);
+
+        setContentView(R.layout.notifications_panel_view);
+
+        mNoNotifsMessage = findViewById(R.id.no_notifications_message);
+        mNotifsList = findViewById(R.id.notifications_list);
+        mNotifsList.setAdapter(mPanelAdapter);
+        mNotifsList.setFocusable(true);
+
+        getLoaderManager().initLoader(0, null,
+                NotificationsSidePanelActivity.this);
+    }
+
+    private void showNoNotificationsMessage() {
+        mNotifsList.setVisibility(View.GONE);
+        mNoNotifsMessage.setVisibility(View.VISIBLE);
+    }
+
+    private void showNotifications() {
+        mNoNotifsMessage.setVisibility(View.GONE);
+        mNotifsList.setVisibility(View.VISIBLE);
+    }
+
+    @Override
+    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+        return new CursorLoader(this, NotificationsContract.CONTENT_URI,
+                TvNotification.PROJECTION, null, null, null);
+    }
+
+    @Override
+    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+        mPanelAdapter.changeCursor(data);
+        if (data != null && data.getCount() > 0) {
+            showNotifications();
+        } else {
+            showNoNotificationsMessage();
+        }
+    }
+
+    @Override
+    public void onLoaderReset(Loader<Cursor> loader) {
+        mPanelAdapter.changeCursor(null);
+    }
+}
diff --git a/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/notifications/NotificationsUtils.java b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/notifications/NotificationsUtils.java
new file mode 100644
index 0000000..9f7da82
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/notifications/NotificationsUtils.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2018 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.example.sampleleanbacklauncher.notifications;
+
+import android.content.Context;
+import android.content.Intent;
+
+import com.example.sampleleanbacklauncher.LauncherConstants;
+
+import java.util.List;
+
+public final class NotificationsUtils {
+    static void dismissNotification(Context context, String key) {
+        Intent dismiss = new Intent(Intent.ACTION_DELETE);
+        dismiss.setPackage(LauncherConstants.TVRECOMMENDATIONS_PACKAGE_NAME);
+
+        dismiss.putExtra(NotificationsContract.NOTIFICATION_KEY, key);
+        context.sendBroadcast(dismiss);
+    }
+
+    static void openNotification(Context context, String key) {
+        Intent open = new Intent(Intent.ACTION_VIEW);
+        open.setPackage(LauncherConstants.TVRECOMMENDATIONS_PACKAGE_NAME);
+        open.putExtra(NotificationsContract.NOTIFICATION_KEY, key);
+        context.sendBroadcast(open);
+    }
+}
diff --git a/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/notifications/TvNotification.java b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/notifications/TvNotification.java
new file mode 100644
index 0000000..32deaba
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/notifications/TvNotification.java
@@ -0,0 +1,192 @@
+package com.example.sampleleanbacklauncher.notifications;
+
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.drawable.Icon;
+import android.os.Parcel;
+
+public class TvNotification {
+    /**
+     * This projection MUST be used for the query when using {@link #fromCursor(Cursor)}.
+     */
+    public static final String[] PROJECTION =
+            {NotificationsContract.COLUMN_SBN_KEY,
+                    NotificationsContract.COLUMN_PACKAGE_NAME,
+                    NotificationsContract.COLUMN_NOTIF_TITLE,
+                    NotificationsContract.COLUMN_NOTIF_TEXT,
+                    NotificationsContract.COLUMN_DISMISSIBLE,
+                    NotificationsContract.COLUMN_ONGOING,
+                    NotificationsContract.COLUMN_SMALL_ICON,
+                    NotificationsContract.COLUMN_CHANNEL,
+                    NotificationsContract.COLUMN_PROGRESS,
+                    NotificationsContract.COLUMN_PROGRESS_MAX,
+                    NotificationsContract.COLUMN_HAS_CONTENT_INTENT,
+                    NotificationsContract.COLUMN_BIG_PICTURE,
+                    NotificationsContract.COLUMN_CONTENT_BUTTON_LABEL,
+                    NotificationsContract.COLUMN_DISMISS_BUTTON_LABEL,
+                    NotificationsContract.COLUMN_TAG};
+
+    public static final int COLUMN_INDEX_KEY = 0;
+    public static final int COLUMN_INDEX_PACKAGE_NAME = 1;
+    public static final int COLUMN_INDEX_NOTIF_TITLE = 2;
+    public static final int COLUMN_INDEX_NOTIF_TEXT = 3;
+    public static final int COLUMN_INDEX_DISMISSIBLE = 4;
+    public static final int COLUMN_INDEX_ONGOING = 5;
+    public static final int COLUMN_INDEX_SMALL_ICON = 6;
+    public static final int COLUMN_INDEX_CHANNEL = 7;
+    public static final int COLUMN_INDEX_PROGRESS = 8;
+    public static final int COLUMN_INDEX_PROGRESS_MAX = 9;
+    public static final int COLUMN_INDEX_HAS_CONTENT_INTENT = 10;
+    public static final int COLUMN_INDEX_BIG_PICTURE = 11;
+    public static final int COLUMN_INDEX_CONTENT_BUTTON_LABEL = 12;
+    public static final int COLUMN_INDEX_DISMISS_BUTTON_LABEL = 13;
+    public static final int COLUMN_INDEX_TAG = 14;
+
+    private String mNotificationKey;
+    private String mPackageName;
+    private String mTitle;
+    private String mText;
+    private boolean mDismissible;
+    private boolean mIsOngoing;
+    private Icon mSmallIcon;
+    private int mChannel;
+    private int mProgress;
+    private int mProgressMax;
+    private boolean mHasContentIntent;
+    private Bitmap mBigPicture;
+    private String mContentButtonLabel;
+    private String mDismissButtonLabel;
+    private String mTag;
+
+    public TvNotification(String key, String packageName, String title, String text,
+                          boolean dismissible, boolean ongoing, Icon smallIcon, int channel,
+                          int progress, int progressMax, boolean hasContentIntent, Bitmap bigPicture,
+                          String contentButtonLabel, String dismissButtonLabel, String tag) {
+        mNotificationKey = key;
+        mPackageName = packageName;
+        mTitle = title;
+        mText = text;
+        mDismissible = dismissible;
+        mIsOngoing = ongoing;
+        mSmallIcon = smallIcon;
+        mChannel = channel;
+        mProgress = progress;
+        mProgressMax = progressMax;
+        mHasContentIntent = hasContentIntent;
+        mBigPicture = bigPicture;
+        mContentButtonLabel = contentButtonLabel;
+        mDismissButtonLabel = dismissButtonLabel;
+        mTag = tag;
+    }
+
+    public String getNotificationKey() {
+        return mNotificationKey;
+    }
+
+    public String getPackageName() {
+        return mPackageName;
+    }
+
+    public String getTitle() {
+        return mTitle;
+    }
+
+    public String getText() {
+        return mText;
+    }
+
+    public boolean isDismissible() {
+        return mDismissible;
+    }
+
+    public boolean isOngoing() {
+        return mIsOngoing;
+    }
+
+    public Icon getSmallIcon() {
+        return mSmallIcon;
+    }
+
+    public int getChannel() {
+        return mChannel;
+    }
+
+    public int getProgress() {
+        return mProgress;
+    }
+
+    public int getProgressMax() {
+        return mProgressMax;
+    }
+
+    public boolean hasContentIntent() {
+        return mHasContentIntent;
+    }
+
+    public Bitmap getBigPicture() {
+        return mBigPicture;
+    }
+
+    public String getContentButtonLabel() {
+        return mContentButtonLabel;
+    }
+
+    public String getDismissButtonLabel() {
+        return mDismissButtonLabel;
+    }
+
+    public String getTag() {
+        return mTag;
+    }
+
+    // Converts cursor returned from query with PROJECTION
+    public static TvNotification fromCursor(Cursor cursor) {
+        int index = 0;
+        String key = cursor.getString(index++);
+        String packageName = cursor.getString(index++);
+        String title = cursor.getString(index++);
+        String text = cursor.getString(index++);
+        boolean dismissible = cursor.getInt(index++) != 0;
+        boolean ongoing = cursor.getInt(index++) != 0;
+        byte[] smallIconData = cursor.getBlob(index++);
+        Icon smallIcon = getIconFromBytes(smallIconData);
+
+        int channel = cursor.getInt(index++);
+        int progress = cursor.getInt(index++);
+        int progressMax = cursor.getInt(index++);
+        boolean hasContentIntent = cursor.getInt(index++) != 0;
+        byte[] bigPictureData = cursor.getBlob(index++);
+        Bitmap bigPicture = getBitmapFromBytes(bigPictureData);
+        String contentButtonLabel = cursor.getString(index++);
+        String dismissButtonLabel = cursor.getString(index++);
+        String tag = cursor.getString(index);
+
+        return new TvNotification(key, packageName, title, text, dismissible, ongoing,
+                smallIcon, channel, progress, progressMax, hasContentIntent, bigPicture,
+                contentButtonLabel, dismissButtonLabel, tag);
+    }
+
+    private static Bitmap getBitmapFromBytes(byte[] blob) {
+        if (blob != null) {
+            Bitmap bitmap = BitmapFactory.decodeByteArray(blob, 0, blob.length);
+            return bitmap;
+        }
+
+        return null;
+    }
+
+    private static Icon getIconFromBytes(byte[] blob) {
+        Parcel in = Parcel.obtain();
+        Icon icon = null;
+        if (blob != null) {
+            in.unmarshall(blob, 0, blob.length);
+            in.setDataPosition(0);
+            icon = in.readParcelable(Icon.class.getClassLoader());
+        }
+
+        in.recycle();
+        return icon;
+    }
+}
+
diff --git a/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/search/SearchFragment.java b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/search/SearchFragment.java
new file mode 100644
index 0000000..b8285f6
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/search/SearchFragment.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2016 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.example.sampleleanbacklauncher.search;
+
+import android.app.Fragment;
+import android.content.ActivityNotFoundException;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.graphics.Outline;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.Trace;
+import androidx.annotation.Nullable;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewOutlineProvider;
+import android.widget.Toast;
+
+import com.example.sampleleanbacklauncher.R;
+
+public class SearchFragment extends Fragment
+        implements View.OnClickListener, View.OnFocusChangeListener {
+    private static final String TAG = "SearchFragment";
+
+    private static final String EXTRA_SEARCH_TYPE = "search_type";
+
+    private static final int SEARCH_TYPE_VOICE = 1;
+    private static final int SEARCH_TYPE_KEYBOARD = 2;
+
+    private View mSearchOrbVoice;
+    private View mSearchOrbKeyboard;
+
+    public static SearchFragment newInstance() {
+        Bundle args = new Bundle();
+
+        SearchFragment fragment = new SearchFragment();
+        fragment.setArguments(args);
+        return fragment;
+    }
+
+    @Nullable
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        final View root = inflater.inflate(R.layout.search, container, false);
+
+        final ViewOutlineProvider outlineProvider = new ViewOutlineProvider() {
+            @Override
+            public void getOutline(View view, Outline outline) {
+                outline.setOval(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
+            }
+        };
+
+        mSearchOrbVoice = root.findViewById(R.id.search_orb_voice);
+        mSearchOrbVoice.setOnFocusChangeListener(this);
+        mSearchOrbVoice.setOutlineProvider(outlineProvider);
+        mSearchOrbVoice.setClipToOutline(true);
+        mSearchOrbVoice.setOnClickListener(this);
+
+        mSearchOrbKeyboard = root.findViewById(R.id.search_orb_keyboard);
+        mSearchOrbKeyboard.setOnFocusChangeListener(this);
+        mSearchOrbKeyboard.setOutlineProvider(outlineProvider);
+        mSearchOrbKeyboard.setClipToOutline(true);
+        mSearchOrbKeyboard.setOnClickListener(this);
+
+        return root;
+    }
+
+    @Override
+    public void onDestroyView() {
+        super.onDestroyView();
+        mSearchOrbVoice.setOnFocusChangeListener(null);
+        mSearchOrbKeyboard.setOnFocusChangeListener(null);
+    }
+
+    @Override
+    public void onClick(View v) {
+        final Intent intent = new Intent(Intent.ACTION_ASSIST)
+                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                        | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+        intent.putExtra(EXTRA_SEARCH_TYPE,
+                v == mSearchOrbKeyboard ? SEARCH_TYPE_KEYBOARD : SEARCH_TYPE_VOICE);
+        try {
+            startActivity(intent);
+            mSearchOrbVoice.requestFocus();
+        } catch (ActivityNotFoundException e) {
+            Log.e(TAG, "Exception launching intent " + intent, e);
+            Toast.makeText(getContext(), getString(R.string.app_unavailable),
+                    Toast.LENGTH_SHORT).show();
+        }
+    }
+
+    @Override
+    public void onFocusChange(View v, boolean hasFocus) {
+        Trace.beginSection("SearchFragment.onFocusChange");
+        try {
+            final View root = getView();
+            if (root == null) {
+                return;
+            }
+            root.requestRectangleOnScreen(
+                    new Rect(0, 0, root.getMeasuredWidth(), root.getMeasuredHeight()));
+
+            final int visibility = hasFocus ? View.VISIBLE : View.GONE;
+            if (v == mSearchOrbKeyboard) {
+                root.findViewById(R.id.search_text_keyboard).setVisibility(visibility);
+            } else {
+                root.findViewById(R.id.search_text_voice).setVisibility(visibility);
+            }
+
+            final Resources resources = getResources();
+            float elevation = resources.getDimension(hasFocus
+                    ? R.dimen.search_item_focused_z : R.dimen.search_item_unfocused_z);
+            float scale = hasFocus
+                    ? resources.getFraction(R.fraction.search_item_focused_zoom, 1, 1) : 1.0f;
+            int duration = resources.getInteger(R.integer.search_orb_scale_duration_ms);
+
+            v.animate().z(elevation).scaleX(scale).scaleY(scale).setDuration(duration);
+        } finally {
+            Trace.endSection();
+        }
+    }
+}
diff --git a/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/util/LauncherAsyncTaskLoader.java b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/util/LauncherAsyncTaskLoader.java
new file mode 100644
index 0000000..32516f6
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/java/com/example/sampleleanbacklauncher/util/LauncherAsyncTaskLoader.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2016 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.example.sampleleanbacklauncher.util;
+
+import android.content.AsyncTaskLoader;
+import android.content.Context;
+
+public abstract class LauncherAsyncTaskLoader<T> extends AsyncTaskLoader<T> {
+    private T mResult;
+
+    public LauncherAsyncTaskLoader(final Context context) {
+        super(context);
+    }
+
+    @Override
+    protected void onStartLoading() {
+        if (mResult != null) {
+            deliverResult(mResult);
+        }
+        if (takeContentChanged() || mResult == null) {
+            forceLoad();
+        }
+    }
+
+    @Override
+    protected void onStopLoading() {
+        cancelLoad();
+    }
+
+    @Override
+    public void deliverResult(final T data) {
+        if (isReset()) {
+            return;
+        }
+        mResult = data;
+        if (isStarted()) {
+            super.deliverResult(data);
+        }
+    }
+
+    @Override
+    protected void onReset() {
+        super.onReset();
+        onStopLoading();
+        mResult = null;
+    }
+}
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_0_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_0_bar_launcher.png
new file mode 100644
index 0000000..71ccedd
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_0_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_1_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_1_bar_launcher.png
new file mode 100644
index 0000000..62983e6
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_1_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_2_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_2_bar_launcher.png
new file mode 100644
index 0000000..83f5f60
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_2_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_3_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_3_bar_launcher.png
new file mode 100644
index 0000000..b9a1257
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_3_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_4_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_4_bar_launcher.png
new file mode 100644
index 0000000..83eec97
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_4_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_no_internet_0_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_no_internet_0_bar_launcher.png
new file mode 100644
index 0000000..c40f219
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_no_internet_0_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_no_internet_1_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_no_internet_1_bar_launcher.png
new file mode 100644
index 0000000..ced92d4
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_no_internet_1_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_no_internet_2_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_no_internet_2_bar_launcher.png
new file mode 100644
index 0000000..8411980
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_no_internet_2_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_no_internet_3_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_no_internet_3_bar_launcher.png
new file mode 100644
index 0000000..71bc360
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_no_internet_3_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_no_internet_4_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_no_internet_4_bar_launcher.png
new file mode 100644
index 0000000..98b9177
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_no_internet_4_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_no_sim_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_no_sim_launcher.png
new file mode 100644
index 0000000..3a956e6
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_no_sim_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_null_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_null_launcher.png
new file mode 100644
index 0000000..ee0ec27
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_null_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_off_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_off_launcher.png
new file mode 100644
index 0000000..17bc81a
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/cellular_off_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/ethernet_active_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/ethernet_active_launcher.png
new file mode 100644
index 0000000..ef43441
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/ethernet_active_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/ethernet_no_internet_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/ethernet_no_internet_launcher.png
new file mode 100644
index 0000000..1ce500a
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/ethernet_no_internet_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/ic_notifications.png b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/ic_notifications.png
new file mode 100644
index 0000000..cbd0c1e
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/ic_notifications.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/wifi_active_0_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/wifi_active_0_launcher.png
new file mode 100644
index 0000000..9879210
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/wifi_active_0_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/wifi_active_1_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/wifi_active_1_launcher.png
new file mode 100644
index 0000000..709d421
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/wifi_active_1_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/wifi_active_2_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/wifi_active_2_launcher.png
new file mode 100644
index 0000000..98a35c8
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/wifi_active_2_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/wifi_active_3_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/wifi_active_3_launcher.png
new file mode 100644
index 0000000..6036042
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/wifi_active_3_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/wifi_active_4_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/wifi_active_4_launcher.png
new file mode 100644
index 0000000..383c2ba
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/wifi_active_4_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/wifi_no_internet_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/wifi_no_internet_launcher.png
new file mode 100644
index 0000000..eda95d9
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/wifi_no_internet_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/wifi_not_connected_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/wifi_not_connected_launcher.png
new file mode 100644
index 0000000..d95a67d
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-hdpi/wifi_not_connected_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_0_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_0_bar_launcher.png
new file mode 100644
index 0000000..2ae8ade
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_0_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_1_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_1_bar_launcher.png
new file mode 100644
index 0000000..709e05e
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_1_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_2_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_2_bar_launcher.png
new file mode 100644
index 0000000..9f4a7de
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_2_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_3_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_3_bar_launcher.png
new file mode 100644
index 0000000..64d2479
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_3_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_4_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_4_bar_launcher.png
new file mode 100644
index 0000000..d3c6afd
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_4_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_no_internet_0_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_no_internet_0_bar_launcher.png
new file mode 100644
index 0000000..8dc8813
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_no_internet_0_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_no_internet_1_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_no_internet_1_bar_launcher.png
new file mode 100644
index 0000000..a43642a
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_no_internet_1_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_no_internet_2_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_no_internet_2_bar_launcher.png
new file mode 100644
index 0000000..88747c3
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_no_internet_2_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_no_internet_3_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_no_internet_3_bar_launcher.png
new file mode 100644
index 0000000..bce337b
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_no_internet_3_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_no_internet_4_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_no_internet_4_bar_launcher.png
new file mode 100644
index 0000000..6a4fb75
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_no_internet_4_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_no_sim_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_no_sim_launcher.png
new file mode 100644
index 0000000..23035f4
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_no_sim_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_null_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_null_launcher.png
new file mode 100644
index 0000000..ee23581
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_null_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_off_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_off_launcher.png
new file mode 100644
index 0000000..2b666da
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/cellular_off_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/ethernet_active_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/ethernet_active_launcher.png
new file mode 100644
index 0000000..08a16e3
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/ethernet_active_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/ethernet_no_internet_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/ethernet_no_internet_launcher.png
new file mode 100644
index 0000000..1184777
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/ethernet_no_internet_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/ic_notifications.png b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/ic_notifications.png
new file mode 100644
index 0000000..9718ccf
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/ic_notifications.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/wifi_active_0_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/wifi_active_0_launcher.png
new file mode 100644
index 0000000..cac8e26
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/wifi_active_0_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/wifi_active_1_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/wifi_active_1_launcher.png
new file mode 100644
index 0000000..8b69f9b
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/wifi_active_1_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/wifi_active_2_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/wifi_active_2_launcher.png
new file mode 100644
index 0000000..178aa8a
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/wifi_active_2_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/wifi_active_3_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/wifi_active_3_launcher.png
new file mode 100644
index 0000000..ccd5f0b
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/wifi_active_3_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/wifi_active_4_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/wifi_active_4_launcher.png
new file mode 100644
index 0000000..a694fd8
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/wifi_active_4_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/wifi_no_internet_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/wifi_no_internet_launcher.png
new file mode 100644
index 0000000..eb57262
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/wifi_no_internet_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/wifi_not_connected_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/wifi_not_connected_launcher.png
new file mode 100644
index 0000000..e270196
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-mdpi/wifi_not_connected_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_0_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_0_bar_launcher.png
new file mode 100644
index 0000000..55e4423
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_0_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_1_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_1_bar_launcher.png
new file mode 100644
index 0000000..8085ee6
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_1_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_2_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_2_bar_launcher.png
new file mode 100644
index 0000000..4543409
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_2_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_3_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_3_bar_launcher.png
new file mode 100644
index 0000000..8e0d081
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_3_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_4_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_4_bar_launcher.png
new file mode 100644
index 0000000..87abeb1
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_4_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_no_internet_0_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_no_internet_0_bar_launcher.png
new file mode 100644
index 0000000..6a1c11c
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_no_internet_0_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_no_internet_1_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_no_internet_1_bar_launcher.png
new file mode 100644
index 0000000..82f9a06
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_no_internet_1_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_no_internet_2_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_no_internet_2_bar_launcher.png
new file mode 100644
index 0000000..89e8be7
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_no_internet_2_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_no_internet_3_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_no_internet_3_bar_launcher.png
new file mode 100644
index 0000000..2bf5e9d
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_no_internet_3_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_no_internet_4_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_no_internet_4_bar_launcher.png
new file mode 100644
index 0000000..e6e16ff
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_no_internet_4_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_no_sim_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_no_sim_launcher.png
new file mode 100644
index 0000000..73cd783
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_no_sim_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_null_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_null_launcher.png
new file mode 100644
index 0000000..b140005
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_null_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_off_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_off_launcher.png
new file mode 100644
index 0000000..3246266
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/cellular_off_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/ethernet_active_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/ethernet_active_launcher.png
new file mode 100644
index 0000000..ee00c2b
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/ethernet_active_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/ethernet_no_internet_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/ethernet_no_internet_launcher.png
new file mode 100644
index 0000000..bcf6645
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/ethernet_no_internet_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/ic_notifications.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/ic_notifications.png
new file mode 100644
index 0000000..1e70b63
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/ic_notifications.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/wifi_active_0_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/wifi_active_0_launcher.png
new file mode 100644
index 0000000..e1a1b28
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/wifi_active_0_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/wifi_active_1_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/wifi_active_1_launcher.png
new file mode 100644
index 0000000..8efbf08
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/wifi_active_1_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/wifi_active_2_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/wifi_active_2_launcher.png
new file mode 100644
index 0000000..bb64d06
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/wifi_active_2_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/wifi_active_3_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/wifi_active_3_launcher.png
new file mode 100644
index 0000000..ed91075
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/wifi_active_3_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/wifi_active_4_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/wifi_active_4_launcher.png
new file mode 100644
index 0000000..d1dce01
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/wifi_active_4_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/wifi_no_internet_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/wifi_no_internet_launcher.png
new file mode 100644
index 0000000..c2f34d5
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/wifi_no_internet_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/wifi_not_connected_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/wifi_not_connected_launcher.png
new file mode 100644
index 0000000..3ea207e
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xhdpi/wifi_not_connected_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_0_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_0_bar_launcher.png
new file mode 100644
index 0000000..e89adac
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_0_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_1_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_1_bar_launcher.png
new file mode 100644
index 0000000..b9b5c05
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_1_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_2_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_2_bar_launcher.png
new file mode 100644
index 0000000..33c3f0c
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_2_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_3_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_3_bar_launcher.png
new file mode 100644
index 0000000..bc9f70a
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_3_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_4_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_4_bar_launcher.png
new file mode 100644
index 0000000..6e55476
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_4_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_no_internet_0_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_no_internet_0_bar_launcher.png
new file mode 100644
index 0000000..ce014ca
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_no_internet_0_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_no_internet_1_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_no_internet_1_bar_launcher.png
new file mode 100644
index 0000000..efc8f83
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_no_internet_1_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_no_internet_2_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_no_internet_2_bar_launcher.png
new file mode 100644
index 0000000..c77aeb8
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_no_internet_2_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_no_internet_3_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_no_internet_3_bar_launcher.png
new file mode 100644
index 0000000..e563533
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_no_internet_3_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_no_internet_4_bar_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_no_internet_4_bar_launcher.png
new file mode 100644
index 0000000..d0318c6
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_no_internet_4_bar_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_no_sim_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_no_sim_launcher.png
new file mode 100644
index 0000000..0884e3c
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_no_sim_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_null_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_null_launcher.png
new file mode 100644
index 0000000..72d13cb
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_null_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_off_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_off_launcher.png
new file mode 100644
index 0000000..0177906
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/cellular_off_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/ethernet_active_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/ethernet_active_launcher.png
new file mode 100644
index 0000000..06dc98b
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/ethernet_active_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/ethernet_no_internet_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/ethernet_no_internet_launcher.png
new file mode 100644
index 0000000..d71f28b
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/ethernet_no_internet_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/ic_notifications.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/ic_notifications.png
new file mode 100644
index 0000000..c5fb9ef
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/ic_notifications.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/wifi_active_0_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/wifi_active_0_launcher.png
new file mode 100755
index 0000000..988c426
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/wifi_active_0_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/wifi_active_1_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/wifi_active_1_launcher.png
new file mode 100755
index 0000000..c8a42b7
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/wifi_active_1_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/wifi_active_2_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/wifi_active_2_launcher.png
new file mode 100755
index 0000000..133ca1d
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/wifi_active_2_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/wifi_active_3_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/wifi_active_3_launcher.png
new file mode 100755
index 0000000..1752147
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/wifi_active_3_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/wifi_active_4_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/wifi_active_4_launcher.png
new file mode 100755
index 0000000..a9cf1da
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/wifi_active_4_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/wifi_no_internet_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/wifi_no_internet_launcher.png
new file mode 100644
index 0000000..25f1cdc
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/wifi_no_internet_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/wifi_not_connected_launcher.png b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/wifi_not_connected_launcher.png
new file mode 100644
index 0000000..781e512
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable-xxhdpi/wifi_not_connected_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable/ic_keyboard_blue.xml b/TvSampleLeanbackLauncher/src/main/res/drawable/ic_keyboard_blue.xml
new file mode 100644
index 0000000..f3a8deb
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable/ic_keyboard_blue.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2016 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
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF4285F4"
+        android:pathData="M20,5L4,5c-1.1,0 -1.99,0.9 -1.99,2L2,17c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,7c0,-1.1 -0.9,-2 -2,-2zM11,8h2v2h-2L11,8zM11,11h2v2h-2v-2zM8,8h2v2L8,10L8,8zM8,11h2v2L8,13v-2zM7,13L5,13v-2h2v2zM7,10L5,10L5,8h2v2zM16,17L8,17v-2h8v2zM16,13h-2v-2h2v2zM16,10h-2L14,8h2v2zM19,13h-2v-2h2v2zM19,10h-2L17,8h2v2z"/>
+</vector>
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable/ic_keyboard_grey.xml b/TvSampleLeanbackLauncher/src/main/res/drawable/ic_keyboard_grey.xml
new file mode 100644
index 0000000..7034b32
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable/ic_keyboard_grey.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2016 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
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FFB1B1B1"
+        android:pathData="M20,5L4,5c-1.1,0 -1.99,0.9 -1.99,2L2,17c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,7c0,-1.1 -0.9,-2 -2,-2zM11,8h2v2h-2L11,8zM11,11h2v2h-2v-2zM8,8h2v2L8,10L8,8zM8,11h2v2L8,13v-2zM7,13L5,13v-2h2v2zM7,10L5,10L5,8h2v2zM16,17L8,17v-2h8v2zM16,13h-2v-2h2v2zM16,10h-2L14,8h2v2zM19,13h-2v-2h2v2zM19,10h-2L17,8h2v2z"/>
+</vector>
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable/ic_mic_color.xml b/TvSampleLeanbackLauncher/src/main/res/drawable/ic_mic_color.xml
new file mode 100644
index 0000000..4725dab
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable/ic_mic_color.xml
@@ -0,0 +1,34 @@
+<!--
+  ~ Copyright (C) 2016 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
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.0">
+    <path
+        android:pathData="M22,37.84h4V44h-4z"
+        android:fillColor="#34A853"/>
+    <path
+        android:pathData="M24,30c3.31,0 5.98,-2.69 5.98,-6L30,10c0,-3.32 -2.68,-6 -6,-6 -3.31,0 -6,2.68 -6,6v14c0,3.31 2.69,6 6,6z"
+        android:fillColor="#4285F4"/>
+    <path
+        android:pathData="M24.01,34c-2.76,0 -5.26,-1.12 -7.07,-2.93l-2.82,2.82c2.53,2.54 6.04,4.11 9.91,4.11 7.73,0 13.95,-6.27 13.95,-14h-4c-0.01,5.52 -4.45,10 -9.97,10z"
+        android:fillColor="#EA4335"/>
+    <path
+        android:pathData="M14.01,24h-3.99c0,3.86 1.56,7.36 4.09,9.89l2.82,-2.82c-1.81,-1.81 -2.92,-4.31 -2.92,-7.07z"
+        android:fillColor="#F4B400"/>
+</vector>
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable/ic_remove_circle_black.xml b/TvSampleLeanbackLauncher/src/main/res/drawable/ic_remove_circle_black.xml
new file mode 100644
index 0000000..3ed2d55
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable/ic_remove_circle_black.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM17,13L7,13v-2h10v2z"/>
+</vector>
\ No newline at end of file
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable/keyboard_search.xml b/TvSampleLeanbackLauncher/src/main/res/drawable/keyboard_search.xml
new file mode 100644
index 0000000..cfde6dc
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable/keyboard_search.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2016 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
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_focused="true" android:drawable="@drawable/ic_keyboard_blue" />
+    <item android:drawable="@drawable/ic_keyboard_grey" />
+</selector>
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable/notification_background_left.xml b/TvSampleLeanbackLauncher/src/main/res/drawable/notification_background_left.xml
new file mode 100644
index 0000000..a663519
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable/notification_background_left.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2018 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
+  -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_focused="true" android:drawable="@drawable/tab_background_left"/>
+</selector>
\ No newline at end of file
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable/notification_background_right.xml b/TvSampleLeanbackLauncher/src/main/res/drawable/notification_background_right.xml
new file mode 100644
index 0000000..b28f062
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable/notification_background_right.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2018 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
+  -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_focused="true" android:drawable="@drawable/tab_background_right"/>
+</selector>
\ No newline at end of file
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable/tab_background_left.xml b/TvSampleLeanbackLauncher/src/main/res/drawable/tab_background_left.xml
new file mode 100644
index 0000000..469826a
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable/tab_background_left.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2018 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
+  -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <solid android:color="@color/notification_selection_color" />
+    <corners android:bottomLeftRadius="100dp" android:topLeftRadius="100dp" />
+</shape>
\ No newline at end of file
diff --git a/TvSampleLeanbackLauncher/src/main/res/drawable/tab_background_right.xml b/TvSampleLeanbackLauncher/src/main/res/drawable/tab_background_right.xml
new file mode 100644
index 0000000..b0bd1ff
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/drawable/tab_background_right.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 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
+  -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <solid android:color="@color/notification_selection_color" />
+    <corners android:bottomRightRadius="100dp" android:topRightRadius="100dp" />
+</shape>
\ No newline at end of file
diff --git a/TvSampleLeanbackLauncher/src/main/res/layout/app_launch_row.xml b/TvSampleLeanbackLauncher/src/main/res/layout/app_launch_row.xml
new file mode 100644
index 0000000..23ac572
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/layout/app_launch_row.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2016 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
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_height="wrap_content"
+    android:layout_width="match_parent"
+    android:orientation="vertical"
+    android:paddingStart="@dimen/overscan_horizontal"
+    android:paddingEnd="@dimen/overscan_horizontal"
+    android:clipToPadding="false"
+    android:clipChildren="false">
+    <TextView
+        android:id="@+id/row_title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:padding="@dimen/app_row_title_padding"
+        android:text="rowTitle"
+        android:textSize="@dimen/app_row_title_text_size" />
+    <androidx.leanback.widget.HorizontalGridView
+        android:id="@android:id/list"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/app_item_height" />
+</LinearLayout>
diff --git a/TvSampleLeanbackLauncher/src/main/res/layout/launch_item.xml b/TvSampleLeanbackLauncher/src/main/res/layout/launch_item.xml
new file mode 100644
index 0000000..4f52c7e
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/layout/launch_item.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2016 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
+  -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/frame"
+    android:layout_width="@dimen/app_item_width"
+    android:layout_height="@dimen/app_item_height"
+    android:background="?android:attr/selectableItemBackground"
+    android:clickable="true"
+    android:focusable="true"
+    android:padding="@dimen/app_item_padding"
+    >
+    <ImageView
+        android:id="@+id/banner"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"/>
+    <LinearLayout
+        android:id="@+id/ll"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:orientation="vertical"
+        >
+        <ImageView
+            android:id="@+id/icon"
+            android:layout_width="@dimen/app_icon_size"
+            android:layout_height="@dimen/app_icon_size"
+            android:layout_gravity="center_horizontal"
+            android:layout_marginBottom="@dimen/app_icon_margin"
+        />
+        <TextView
+            android:id="@+id/label"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_horizontal"
+        />
+    </LinearLayout>
+</FrameLayout>
diff --git a/TvSampleLeanbackLauncher/src/main/res/layout/launcher.xml b/TvSampleLeanbackLauncher/src/main/res/layout/launcher.xml
new file mode 100644
index 0000000..1b05005
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/layout/launcher.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2016 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
+  -->
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/mostly_transparent_wrapper"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:clipToPadding="false"
+    android:clipChildren="false">
+    <ScrollView
+        android:id="@+id/scroll_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_marginTop="@dimen/overscan_vertical"
+        android:layout_marginBottom="@dimen/overscan_vertical"
+        android:clipToPadding="false"
+        android:clipChildren="false"
+        android:scrollbars="none">
+        <LinearLayout
+            android:id="@+id/launcher_container"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical"
+            android:clipChildren="false"
+            android:clipToPadding="false"/>
+    </ScrollView>
+</FrameLayout>
diff --git a/TvSampleLeanbackLauncher/src/main/res/layout/leanback_banner_preference.xml b/TvSampleLeanbackLauncher/src/main/res/layout/leanback_banner_preference.xml
new file mode 100644
index 0000000..2c7593f
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/layout/leanback_banner_preference.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2016 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
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/container"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:background="?android:attr/selectableItemBackground"
+    android:clickable="true"
+    android:focusable="true"
+    android:descendantFocusability="blocksDescendants"
+    android:orientation="horizontal"
+    android:paddingStart="@dimen/lb_preference_item_padding_start"
+    android:paddingEnd="@dimen/lb_preference_item_padding_end" >
+
+    <FrameLayout
+        android:layout_width="@dimen/preference_item_banner_width"
+        android:layout_height="@dimen/preference_item_banner_height"
+        android:layout_gravity="center_vertical"
+        android:layout_marginEnd="@dimen/lb_preference_item_icon_margin_end">
+        <FrameLayout
+            android:id="@+id/icon_frame"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:background="@color/preference_item_banner_background">
+            <ImageView
+                android:id="@android:id/icon"
+                android:layout_width="@dimen/preference_item_icon_size"
+                android:layout_height="@dimen/preference_item_icon_size"
+                android:layout_gravity="center"/>
+        </FrameLayout>
+
+        <ImageView
+            android:id="@+id/banner"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"/>
+    </FrameLayout>
+
+    <LinearLayout android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:orientation="vertical">
+        <Space android:layout_width="0dp" android:layout_height="@dimen/lb_preference_item_text_space_top" />
+        <TextView
+            android:id="@android:id/title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginBottom="@dimen/lb_preference_item_primary_text_margin_bottom"
+            android:fontFamily="sans-serif-condensed"
+            android:textColor="@color/lb_preference_item_primary_text_color"
+            android:textSize="@dimen/lb_preference_item_primary_text_size"/>
+        <TextView
+            android:id="@android:id/summary"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:fontFamily="sans-serif-condensed"
+            android:textColor="@color/lb_preference_item_secondary_text_color"
+            android:textSize="@dimen/lb_preference_item_secondary_text_size"
+            android:maxLines="4" />
+        <Space android:layout_width="0dp" android:layout_height="@dimen/lb_preference_item_text_space_bottom" />
+    </LinearLayout>
+
+    <!-- Preference should place its actual preference widget here. -->
+    <LinearLayout android:id="@android:id/widget_frame"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:gravity="center_vertical"
+        android:orientation="vertical" />
+
+</LinearLayout>
diff --git a/TvSampleLeanbackLauncher/src/main/res/layout/notification_panel_item.xml b/TvSampleLeanbackLauncher/src/main/res/layout/notification_panel_item.xml
new file mode 100644
index 0000000..1acdd1f
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/layout/notification_panel_item.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2018 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
+  -->
+<com.example.sampleleanbacklauncher.notifications.NotificationPanelItemView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical">
+
+    <include
+        layout="@layout/notification_panel_item_main_content"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/notification_panel_item_height" />
+
+    <include
+        layout="@layout/notification_panel_item_expanded_text"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+
+</com.example.sampleleanbacklauncher.notifications.NotificationPanelItemView>
\ No newline at end of file
diff --git a/TvSampleLeanbackLauncher/src/main/res/layout/notification_panel_item_dismissible.xml b/TvSampleLeanbackLauncher/src/main/res/layout/notification_panel_item_dismissible.xml
new file mode 100644
index 0000000..1269227
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/layout/notification_panel_item_dismissible.xml
@@ -0,0 +1,50 @@
+<com.example.sampleleanbacklauncher.notifications.NotificationPanelDismissibleItemView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical">
+
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+
+        <include
+            layout="@layout/notification_panel_item_main_content"
+            android:layout_width="@dimen/notification_panel_item_container_width"
+            android:layout_height="@dimen/notification_panel_item_height" />
+
+        <LinearLayout
+            android:id="@+id/dismiss_button"
+            android:layout_width="@dimen/notification_panel_item_dismiss_button_width"
+            android:layout_height="@dimen/notification_panel_item_height"
+            android:background="@drawable/notification_background_left"
+            android:clickable="true"
+            android:focusable="true">
+
+            <ImageView
+                android:layout_width="@dimen/notification_panel_item_dismiss_icon_size"
+                android:layout_height="@dimen/notification_panel_item_dismiss_icon_size"
+                android:layout_gravity="center_vertical"
+                android:layout_marginEnd="@dimen/notification_panel_item_dismiss_icon_margin_end"
+                android:layout_marginStart="@dimen/notification_panel_item_dismiss_icon_margin_start"
+                android:src="@drawable/ic_remove_circle_black"
+                android:tint="@color/notification_icon_tint" />
+
+            <TextView
+                android:id="@+id/dismiss_text"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_vertical"
+                android:fontFamily="sans-serif-condensed"
+                android:textColor="@color/notification_dimiss_text_color"
+                android:textSize="@dimen/notification_panel_item_primary_text_size"
+                android:visibility="gone" />
+        </LinearLayout>
+    </LinearLayout>
+
+    <include
+        layout="@layout/notification_panel_item_expanded_text"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+
+</com.example.sampleleanbacklauncher.notifications.NotificationPanelDismissibleItemView>
\ No newline at end of file
diff --git a/TvSampleLeanbackLauncher/src/main/res/layout/notification_panel_item_expanded_text.xml b/TvSampleLeanbackLauncher/src/main/res/layout/notification_panel_item_expanded_text.xml
new file mode 100644
index 0000000..502e125
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/layout/notification_panel_item_expanded_text.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2018 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
+  -->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/notification_expanded_text"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_gravity="start"
+    android:fontFamily="sans-serif-condensed"
+    android:paddingBottom="@dimen/notification_panel_item_padding_bottom"
+    android:paddingEnd="@dimen/notification_panel_item_expanded_text_padding_end"
+    android:paddingStart="@dimen/notification_panel_item_expanded_text_padding_start"
+    android:paddingTop="@dimen/notification_panel_item_padding_top"
+    android:textColor="@color/secondary_text_color"
+    android:textSize="@dimen/notification_panel_item_secondary_text_size"
+    android:visibility="gone" />
\ No newline at end of file
diff --git a/TvSampleLeanbackLauncher/src/main/res/layout/notification_panel_item_main_content.xml b/TvSampleLeanbackLauncher/src/main/res/layout/notification_panel_item_main_content.xml
new file mode 100644
index 0000000..b6d08bd
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/layout/notification_panel_item_main_content.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2018 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
+  -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/notification_container"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:background="?android:attr/selectableItemBackground"
+    android:nextFocusLeft="@id/notification_container"
+    android:nextFocusRight="@id/notification_container"
+    android:clickable="true"
+    android:focusable="true"
+    android:orientation="horizontal"
+    android:paddingBottom="@dimen/notification_panel_item_padding_bottom"
+    android:paddingEnd="@dimen/notification_panel_item_padding_end"
+    android:paddingStart="@dimen/notification_panel_item_padding_start"
+    android:paddingTop="@dimen/notification_panel_item_padding_top">
+
+    <ImageView
+        android:id="@+id/notification_icon"
+        android:layout_width="@dimen/notification_panel_item_icon_size"
+        android:layout_height="@dimen/notification_panel_item_icon_size"
+        android:layout_gravity="center_vertical"
+        android:layout_marginEnd="@dimen/notification_panel_item_icon_margin_end"
+        android:tint="@color/notification_icon_tint" />
+
+    <LinearLayout
+        android:layout_width="@dimen/notification_panel_item_text_width"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:orientation="vertical">
+
+        <TextView
+            android:id="@+id/notification_title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginBottom="@dimen/notification_panel_item_text_margin_bottom"
+            android:ellipsize="end"
+            android:fontFamily="sans-serif-condensed"
+            android:maxLines="1"
+            android:textColor="@color/primary_text_color"
+            android:textSize="@dimen/notification_panel_item_primary_text_size" />
+
+        <TextView
+            android:id="@+id/notification_text"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:ellipsize="end"
+            android:fontFamily="sans-serif-condensed"
+            android:maxLines="1"
+            android:textColor="@color/secondary_text_color"
+            android:textSize="@dimen/notification_panel_item_secondary_text_size" />
+    </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/TvSampleLeanbackLauncher/src/main/res/layout/notifications_panel_view.xml b/TvSampleLeanbackLauncher/src/main/res/layout/notifications_panel_view.xml
new file mode 100644
index 0000000..e87343c
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/layout/notifications_panel_view.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 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
+  -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/notifications_panel_view"
+    android:layout_width="@dimen/notification_panel_width"
+    android:layout_height="match_parent"
+    android:layout_gravity="end"
+    android:animateLayoutChanges="true"
+    android:background="@color/notification_panel_background"
+    android:orientation="vertical">
+
+    <FrameLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:background="?attr/defaultBrandColor"
+        android:elevation="@dimen/lb_preference_decor_title_container_elevation"
+        android:transitionGroup="false">
+
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="@dimen/lb_preference_decor_title_text_height"
+            android:layout_marginEnd="@dimen/lb_preference_decor_title_margin_end"
+            android:layout_marginStart="@dimen/lb_preference_decor_title_margin_start"
+            android:layout_marginTop="@dimen/lb_preference_decor_title_margin_top"
+            android:fontFamily="sans-serif-condensed"
+            android:gravity="center_vertical"
+            android:maxLines="1"
+            android:text="@string/system_notifications"
+            android:textColor="?android:attr/textColorPrimary"
+            android:textSize="@dimen/lb_preference_decor_title_text_size" />
+    </FrameLayout>
+
+    <TextView
+        android:id="@+id/no_notifications_message"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:paddingBottom="@dimen/notification_panel_vertical_padding"
+        android:paddingEnd="@dimen/notification_panel_horizontal_padding"
+        android:paddingStart="@dimen/notification_panel_horizontal_padding"
+        android:paddingTop="@dimen/notification_panel_title_padding"
+        android:text="@string/no_notifications_message"
+        android:textColor="@color/secondary_text_color"
+        android:visibility="gone" />
+
+    <androidx.leanback.widget.VerticalGridView
+        android:id="@+id/notifications_list"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:clipToPadding="false">
+
+        <requestFocus />
+    </androidx.leanback.widget.VerticalGridView>
+</LinearLayout>
\ No newline at end of file
diff --git a/TvSampleLeanbackLauncher/src/main/res/layout/pref_wall_of_text.xml b/TvSampleLeanbackLauncher/src/main/res/layout/pref_wall_of_text.xml
new file mode 100644
index 0000000..90d72cd
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/layout/pref_wall_of_text.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2016 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
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/container"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:background="?android:attr/selectableItemBackground"
+    android:clickable="false"
+    android:focusable="false"
+    android:descendantFocusability="blocksDescendants"
+    android:orientation="horizontal"
+    android:paddingStart="@dimen/lb_preference_item_padding_start"
+    android:paddingEnd="@dimen/lb_preference_item_padding_end" >
+
+    <LinearLayout android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:orientation="vertical">
+        <Space android:layout_width="0dp" android:layout_height="@dimen/lb_preference_item_text_space_top" />
+        <TextView
+            android:id="@android:id/title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginBottom="@dimen/lb_preference_item_primary_text_margin_bottom"
+            android:fontFamily="sans-serif-condensed"
+            android:textColor="@color/lb_preference_item_secondary_text_color"
+            android:textSize="@dimen/lb_preference_item_secondary_text_size" />
+        <Space android:layout_width="0dp" android:layout_height="@dimen/lb_preference_item_text_space_bottom" />
+    </LinearLayout>
+
+</LinearLayout>
diff --git a/TvSampleLeanbackLauncher/src/main/res/layout/search.xml b/TvSampleLeanbackLauncher/src/main/res/layout/search.xml
new file mode 100644
index 0000000..85cf306
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/layout/search.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2016 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
+  -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/search_row"
+    android:layout_width="match_parent"
+    android:layout_height="@dimen/search_row_height"
+    android:paddingStart="@dimen/search_row_padding_start"
+    android:paddingEnd="@dimen/overscan_horizontal"
+    android:paddingTop="@dimen/search_row_padding_top"
+    android:clipChildren="false"
+    android:clipToPadding="false" >
+    <ImageView
+        android:id="@+id/search_orb_voice"
+        android:layout_width="@dimen/search_item_size"
+        android:layout_height="@dimen/search_item_size"
+        android:background="@android:color/white"
+        android:clickable="true"
+        android:contentDescription="@string/search_click_to_speak"
+        android:elevation="@dimen/search_item_unfocused_z"
+        android:focusable="true"
+        android:focusedByDefault="true"
+        android:padding="@dimen/search_item_padding"
+        android:src="@drawable/ic_mic_color"/>
+    <ImageView
+        android:id="@+id/search_orb_keyboard"
+        android:layout_width="@dimen/search_item_size"
+        android:layout_height="@dimen/search_item_size"
+        android:layout_marginStart="@dimen/search_item_spacing"
+        android:background="@android:color/white"
+        android:clickable="true"
+        android:contentDescription="@string/search_click_to_type"
+        android:elevation="@dimen/search_item_unfocused_z"
+        android:focusable="true"
+        android:focusedByDefault="false"
+        android:padding="@dimen/search_item_padding"
+        android:src="@drawable/keyboard_search"/>
+    <TextView
+        android:id="@+id/search_text_voice"
+        android:layout_width="wrap_content"
+        android:layout_height="@dimen/search_item_size"
+        android:layout_marginStart="@dimen/search_label_spacing"
+        android:gravity="center_vertical"
+        android:text="@string/search_click_to_speak"
+        android:textColor="@android:color/white"
+        android:textSize="@dimen/search_label_text_size"
+        android:textStyle="italic"/>
+    <TextView
+        android:id="@+id/search_text_keyboard"
+        android:layout_width="wrap_content"
+        android:layout_height="@dimen/search_item_size"
+        android:layout_marginStart="@dimen/search_label_spacing"
+        android:gravity="center_vertical"
+        android:text="@string/search_click_to_type"
+        android:textColor="@android:color/white"
+        android:textSize="@dimen/search_label_text_size"
+        android:textStyle="italic"/>
+</LinearLayout>
diff --git a/TvSampleLeanbackLauncher/src/main/res/mipmap-hdpi/ic_launcher.png b/TvSampleLeanbackLauncher/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..cde69bc
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/mipmap-mdpi/ic_launcher.png b/TvSampleLeanbackLauncher/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c133a0c
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/mipmap-xhdpi/ic_launcher.png b/TvSampleLeanbackLauncher/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..bfa42f0
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/mipmap-xxhdpi/ic_launcher.png b/TvSampleLeanbackLauncher/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..324e72c
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/TvSampleLeanbackLauncher/src/main/res/values/arrays.xml b/TvSampleLeanbackLauncher/src/main/res/values/arrays.xml
new file mode 100644
index 0000000..d93b991
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/values/arrays.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2016 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
+  -->
+
+<resources>
+    <!-- Out of box ordering for system apps. You probably shouldn't modify this [DO NOT TRANSLATE] -->
+    <string-array name="oob_order" translatable="false">
+        <item>com.android.vending</item>
+        <item>com.google.android.videos</item>
+        <item>com.google.android.music</item>
+        <item>com.google.android.youtube.tv</item>
+        <item>com.google.android.play.games</item>
+    </string-array>
+</resources>
diff --git a/TvSampleLeanbackLauncher/src/main/res/values/colors.xml b/TvSampleLeanbackLauncher/src/main/res/values/colors.xml
new file mode 100644
index 0000000..b5d956b
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/values/colors.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2016 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
+  -->
+
+<resources>
+    <color name="preference_item_banner_background">#455a64</color>
+
+    <color name="primary_text_color">#e5e5e5</color>
+    <color name="secondary_text_color">#787b7f</color>
+
+    <color name="notification_text_color">#787b7f</color>
+    <color name="notification_background">#FFCCCCCC</color>
+    <color name="notification_heads_up_decorator">#1784d7</color>
+    <color name="notification_heads_up_background">#c0000000</color>
+
+    <color name="notification_panel_background">#263238</color>
+    <color name="notification_selection_color">#26eeeeee</color>
+    <color name="notification_dimiss_text_color">#b2eeeeee</color>
+    <color name="notification_icon_tint">#FFFFFF</color>
+    <color name="notification_expanded_text_background">#0aeeeeee</color>
+    <color name="notification_progress_stroke_color">#17ffff</color>
+    <color name="notification_progress_stroke_max_color">#3317ffff</color>
+</resources>
diff --git a/TvSampleLeanbackLauncher/src/main/res/values/dimens.xml b/TvSampleLeanbackLauncher/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..8936f39
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/values/dimens.xml
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2016 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
+  -->
+
+<resources>
+    <dimen name="overscan_vertical">27dp</dimen>
+    <dimen name="overscan_horizontal">48dp</dimen>
+
+    <dimen name="app_item_width">190dp</dimen>
+    <dimen name="app_item_height">120dp</dimen>
+    <dimen name="app_item_padding">15dp</dimen>
+    <dimen name="app_row_title_text_size">20sp</dimen>
+    <dimen name="app_row_title_padding">5dp</dimen>
+    <dimen name="app_icon_size">48dp</dimen>
+    <dimen name="app_icon_margin">10dp</dimen>
+
+    <dimen name="search_row_height">108dp</dimen>
+    <dimen name="search_row_padding_start">56dp</dimen>
+    <dimen name="search_row_padding_top">4dp</dimen>
+    <dimen name="search_item_spacing">28dp</dimen>
+    <dimen name="search_item_size">52dp</dimen>
+    <dimen name="search_item_padding">14dp</dimen>
+    <dimen name="search_item_focused_z">8dp</dimen>
+    <dimen name="search_item_unfocused_z">2dp</dimen>
+    <item name="search_item_focused_zoom" type="fraction">120%</item>
+    <item name="search_orb_scale_duration_ms" type="integer">150</item>
+    <dimen name="search_label_spacing">24dp</dimen>
+    <dimen name="search_label_text_size">18sp</dimen>
+
+    <dimen name="rec_item_padding">15dp</dimen>
+    <dimen name="rec_card_image_height">156dp</dimen>
+    <dimen name="rec_card_image_min_width">108dp</dimen>
+    <dimen name="rec_card_image_max_width">313dp</dimen>
+    <dimen name="rec_title_size">16sp</dimen>
+    <dimen name="rec_subtitle_size">12sp</dimen>
+    <dimen name="rec_icon_size">16dp</dimen>
+
+    <dimen name="now_playing_padding">15dp</dimen>
+    <dimen name="now_playing_text_margin">15dp</dimen>
+    <dimen name="now_playing_progress_margin">15dp</dimen>
+    <dimen name="now_playing_image_height">156dp</dimen>
+    <dimen name="now_playing_image_min_width">108dp</dimen>
+    <dimen name="now_playing_image_max_width">313dp</dimen>
+
+    <dimen name="preference_item_banner_width">64dp</dimen>
+    <dimen name="preference_item_banner_height">36dp</dimen>
+    <dimen name="preference_item_icon_size">24dp</dimen>
+
+    <dimen name="notification_panel_vertical_padding">6dp</dimen>
+    <dimen name="notification_panel_horizontal_padding">24dp</dimen>
+    <dimen name="notification_panel_width">360dp</dimen>
+    <dimen name="notification_panel_title_padding">24dp</dimen>
+
+    <dimen name="notification_progress_stroke_width">2dp</dimen>
+    <dimen name="notification_progress_circle_size">40dp</dimen>
+    <dimen name="notification_progress_circle_padding_start">20dp</dimen>
+    <dimen name="notification_progress_circle_padding_top">13dp</dimen>
+
+    <dimen name="notification_panel_item_icon_size">32dp</dimen>
+    <dimen name="notification_panel_item_icon_margin_start">14dp</dimen>
+    <dimen name="notification_panel_item_icon_margin_end">14dp</dimen>
+    <dimen name="notification_panel_item_show_button_translate_x">-96dp</dimen>
+    <dimen name="notification_panel_item_dismiss_translate_x">-380dp</dimen>
+    <dimen name="notification_panel_item_margin_start">24dp</dimen>
+    <dimen name="notification_panel_item_margin_bottom">12dp</dimen>
+    <dimen name="notification_panel_item_margin_top">14dp</dimen>
+    <dimen name="notification_panel_item_margin_end">56dp</dimen>
+    <dimen name="notification_panel_item_padding_end">16dp</dimen>
+    <dimen name="notification_panel_item_padding_bottom">12dp</dimen>
+    <dimen name="notification_panel_item_padding_start">24dp</dimen>
+    <dimen name="notification_panel_item_padding_top">14dp</dimen>
+    <dimen name="notification_panel_item_height">64dp</dimen>
+    <dimen name="notification_panel_item_container_width">278dp</dimen>
+    <dimen name="notification_panel_item_text_width">192dp</dimen>
+    <dimen name="notification_panel_item_text_margin_bottom">2dp</dimen>
+    <dimen name="notification_panel_item_primary_text_size">14sp</dimen>
+    <dimen name="notification_panel_item_secondary_text_size">12sp</dimen>
+    <dimen name="notification_panel_item_dismiss_icon_size">22dp</dimen>
+    <dimen name="notification_panel_item_dismiss_button_width">500dp</dimen>
+    <dimen name="notification_panel_item_dismiss_icon_margin_start">15.5dp</dimen>
+    <dimen name="notification_panel_item_dismiss_icon_margin_end">7.5dp</dimen>
+    <dimen name="notification_panel_item_expanded_text_padding_start">71dp</dimen>
+    <dimen name="notification_panel_item_expanded_text_padding_end">50dp</dimen>
+
+    <dimen name="lb_preference_decor_title_text_height">2dp</dimen>
+    <dimen name="lb_preference_decor_title_margin_end">2dp</dimen>
+    <dimen name="lb_preference_decor_title_margin_start">2dp</dimen>
+    <dimen name="lb_preference_decor_title_margin_top">2dp</dimen>
+    <dimen name="lb_preference_decor_title_container_elevation">2dp</dimen>
+</resources>
diff --git a/TvSampleLeanbackLauncher/src/main/res/values/ids.xml b/TvSampleLeanbackLauncher/src/main/res/values/ids.xml
new file mode 100644
index 0000000..41a3670
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/values/ids.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2016 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
+  -->
+
+<resources>
+    <item type="id" name="apps_row"/>
+    <item type="id" name="games_row"/>
+    <item type="id" name="settings_row"/>
+</resources>
diff --git a/TvSampleLeanbackLauncher/src/main/res/values/strings.xml b/TvSampleLeanbackLauncher/src/main/res/values/strings.xml
new file mode 100644
index 0000000..86d3d79
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/values/strings.xml
@@ -0,0 +1,58 @@
+<!--
+  ~ Copyright (C) 2016 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
+  -->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name">Sample Leanback Launcher</string>
+    <string name="apps_row_title">Apps</string>
+    <string name="games_row_title">Games</string>
+    <string name="settings_row_title">Settings</string>
+
+    <string name="network_settings_disconnected">Network Settings</string>
+
+    <string name="app_unavailable">Application unavailable</string>
+
+    <string name="search_click_to_speak">Click to speak</string>
+    <string name="search_click_to_type">Click to type</string>
+
+    <string name="now_playing_state_playing">Now Playing</string>
+    <string name="now_playing_state_paused">Paused</string>
+
+    <!-- Title of dialog which allows user to select which apps will be allowed to post recommendations -->
+    <string name="rec_prefs_title">Recommendations row</string>
+
+    <!-- Description of dialog which allows user to select which apps will be allowed to post recommendations -->
+    <string name="rec_prefs_wall_of_text">Selected sources may display recommendations on the home screen. To hide these, turn off recommendations from specific sources.</string>
+
+    <!-- Recommendations missing reasons -->
+    <string name="rec_missing_empty">No recommendations</string>
+    <string name="rec_missing_disabled">Some recommendation sources disabled</string>
+    <string name="rec_missing_preparing">Preparing recommendations</string>
+
+    <!-- Notifications -->
+    <plurals name="notifications_title">
+        <item quantity="one">%d notification</item>
+        <item quantity="other">%d notifications</item>
+    </plurals>
+
+    <!-- Title displayed at the top of the notifications panel.  -->
+    <string name="system_notifications">Notifications</string>
+
+    <!-- Text displayed in the notifications panel if there are no notifications.  -->
+    <string name="no_notifications_message">No notifications</string>
+
+    <!-- Format for a content description for a notification where the first argument is the notification title and the second is the notification text -->
+    <string name="notification_content_description_format"><xliff:g id="title" example="USB debugging connected">%1$s </xliff:g><xliff:g id="text" example="Select to disable USB debugging.">%2$s</xliff:g></string>
+</resources>
diff --git a/TvSampleLeanbackLauncher/src/main/res/values/styles.xml b/TvSampleLeanbackLauncher/src/main/res/values/styles.xml
new file mode 100644
index 0000000..4dcde15
--- /dev/null
+++ b/TvSampleLeanbackLauncher/src/main/res/values/styles.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2016 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
+  -->
+
+<resources>
+
+    <style name="AppTheme" parent="@style/Theme.Leanback">
+        <item name="android:windowBackground">@android:color/transparent</item>
+        <item name="android:windowShowWallpaper">true</item>
+    </style>
+    <style name="SettingsTheme" parent="@style/Theme.Leanback">
+        <item name="android:windowBackground">@android:color/transparent</item>
+        <item name="android:windowIsTranslucent">true</item>
+        <item name="android:backgroundDimAmount">.8</item>
+        <item name="android:backgroundDimEnabled">true</item>
+        <item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Leanback</item>
+    </style>
+    <style name="NotificationsSidePanel" parent="@style/Theme.Leanback">
+        <item name="android:windowBackground">@android:color/transparent</item>
+        <item name="android:windowIsTranslucent">true</item>
+        <item name="android:backgroundDimAmount">.8</item>
+        <item name="android:backgroundDimEnabled">true</item>
+        <item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Leanback</item>
+    </style>
+</resources>
diff --git a/audio_proxy/Android.bp b/audio_proxy/Android.bp
index 2c33cf2..eca5f07 100644
--- a/audio_proxy/Android.bp
+++ b/audio_proxy/Android.bp
@@ -22,7 +22,7 @@
   ],
 
   shared_libs: [
-    "device.google.atv.audio_proxy-aidl-V1-ndk",
+    "device.google.atv.audio_proxy-aidl-V3-ndk",
     "libbase",
     "libbinder_ndk",
     "libcutils",
diff --git a/audio_proxy/AudioProxyDevice.cpp b/audio_proxy/AudioProxyDevice.cpp
index 4c409a1..1721cdd 100644
--- a/audio_proxy/AudioProxyDevice.cpp
+++ b/audio_proxy/AudioProxyDevice.cpp
@@ -20,9 +20,9 @@
 
 using aidl::device::google::atv::audio_proxy::AudioConfig;
 
-#define CHECK_API(func)                        \
+#define CHECK_API(st, func)                    \
   do {                                         \
-    if (!stream->func) {                       \
+    if (!st->func) {                           \
       LOG(ERROR) << "Undefined API " << #func; \
       return false;                            \
     }                                          \
@@ -31,14 +31,23 @@
 namespace audio_proxy {
 namespace {
 bool isValidStreamOut(const audio_proxy_stream_out_t* stream) {
-  CHECK_API(standby);
-  CHECK_API(pause);
-  CHECK_API(resume);
-  CHECK_API(flush);
-  CHECK_API(drain);
-  CHECK_API(write);
-  CHECK_API(get_presentation_position);
-  CHECK_API(set_volume);
+  CHECK_API(stream, standby);
+  CHECK_API(stream, pause);
+  CHECK_API(stream, resume);
+  CHECK_API(stream, flush);
+  CHECK_API(stream, drain);
+  CHECK_API(stream, write);
+  CHECK_API(stream, get_presentation_position);
+  CHECK_API(stream, set_volume);
+  CHECK_API(stream, get_buffer_size);
+  CHECK_API(stream, get_latency);
+
+  if (stream->v2) {
+    CHECK_API(stream->v2, start);
+    CHECK_API(stream->v2, stop);
+    CHECK_API(stream->v2, create_mmap_buffer);
+    CHECK_API(stream->v2, get_mmap_position);
+  }
 
   return true;
 }
@@ -55,13 +64,18 @@
 
 std::unique_ptr<AudioProxyStreamOut> AudioProxyDevice::openOutputStream(
     const std::string& address, const AudioConfig& aidlConfig, int32_t flags) {
+  audio_proxy_config_v2_t config_v2 = {
+      .buffer_size_bytes = aidlConfig.bufferSizeBytes,
+      .latency_ms = aidlConfig.latencyMs,
+      .extension = nullptr};
+
   audio_proxy_config_t config = {
       .format = static_cast<audio_proxy_format_t>(aidlConfig.format),
       .sample_rate = static_cast<uint32_t>(aidlConfig.sampleRateHz),
       .channel_mask =
           static_cast<audio_proxy_channel_mask_t>(aidlConfig.channelMask),
       .frame_count = 0,
-      .extension = nullptr};
+      .v2 = &config_v2};
 
   // TODO(yucliu): Pass address to the app. For now, the only client app
   // (MediaShell) can use flags to distinguish different streams.
diff --git a/audio_proxy/AudioProxyStreamOut.cpp b/audio_proxy/AudioProxyStreamOut.cpp
index f1f47eb..cb60fe4 100644
--- a/audio_proxy/AudioProxyStreamOut.cpp
+++ b/audio_proxy/AudioProxyStreamOut.cpp
@@ -54,4 +54,53 @@
   mStream->set_volume(mStream, left, right);
 }
 
+int64_t AudioProxyStreamOut::getBufferSizeBytes() {
+  return mStream->get_buffer_size(mStream);
+}
+
+int32_t AudioProxyStreamOut::getLatencyMs() {
+  return mStream->get_latency(mStream);
+}
+
+void AudioProxyStreamOut::start() {
+  if (mStream->v2) {
+    mStream->v2->start(mStream->v2);
+  }
+}
+
+void AudioProxyStreamOut::stop() {
+  if (mStream->v2) {
+    mStream->v2->stop(mStream->v2);
+  }
+}
+
+MmapBufferInfo AudioProxyStreamOut::createMmapBuffer(
+    int32_t minBufferSizeFrames) {
+  MmapBufferInfo aidlInfo;
+  if (!mStream->v2) {
+    return aidlInfo;
+  }
+
+  audio_proxy_mmap_buffer_info_t info =
+      mStream->v2->create_mmap_buffer(mStream->v2, minBufferSizeFrames);
+  aidlInfo.sharedMemoryFd.set(info.shared_memory_fd);
+  aidlInfo.bufferSizeFrames = info.buffer_size_frames;
+  aidlInfo.burstSizeFrames = info.burst_size_frames;
+  aidlInfo.flags = info.flags;
+  return aidlInfo;
+}
+
+PresentationPosition AudioProxyStreamOut::getMmapPosition() {
+  PresentationPosition position;
+  if (!mStream->v2) {
+    return position;
+  }
+
+  int64_t frames = 0;
+  struct timespec ts = {0, 0};
+  mStream->v2->get_mmap_position(mStream->v2, &frames, &ts);
+  position.frames = frames;
+  position.timestamp = {ts.tv_sec, ts.tv_nsec};
+  return position;
+}
 }  // namespace audio_proxy
diff --git a/audio_proxy/AudioProxyStreamOut.h b/audio_proxy/AudioProxyStreamOut.h
index 8a773ae..3ecd4fd 100644
--- a/audio_proxy/AudioProxyStreamOut.h
+++ b/audio_proxy/AudioProxyStreamOut.h
@@ -15,6 +15,8 @@
 #pragma once
 
 #include <aidl/device/google/atv/audio_proxy/AudioDrain.h>
+#include <aidl/device/google/atv/audio_proxy/MmapBufferInfo.h>
+#include <aidl/device/google/atv/audio_proxy/PresentationPosition.h>
 #include <aidl/device/google/atv/audio_proxy/TimeSpec.h>
 
 #include <memory>
@@ -24,6 +26,8 @@
 namespace audio_proxy {
 
 using aidl::device::google::atv::audio_proxy::AudioDrain;
+using aidl::device::google::atv::audio_proxy::MmapBufferInfo;
+using aidl::device::google::atv::audio_proxy::PresentationPosition;
 using aidl::device::google::atv::audio_proxy::TimeSpec;
 
 // C++ friendly wrapper of audio_proxy_stream_out. It handles type conversion
@@ -45,6 +49,14 @@
 
   void setVolume(float left, float right);
 
+  int64_t getBufferSizeBytes();
+  int32_t getLatencyMs();
+
+  void start();
+  void stop();
+  MmapBufferInfo createMmapBuffer(int32_t minBufferSizeFrames);
+  PresentationPosition getMmapPosition();
+
  private:
   audio_proxy_stream_out_t* const mStream;
   audio_proxy_device_t* const mDevice;
diff --git a/audio_proxy/OutputStreamImpl.cpp b/audio_proxy/OutputStreamImpl.cpp
index 07eaff6..935a879 100644
--- a/audio_proxy/OutputStreamImpl.cpp
+++ b/audio_proxy/OutputStreamImpl.cpp
@@ -23,7 +23,6 @@
 #include "AudioProxyStreamOut.h"
 
 using aidl::device::google::atv::audio_proxy::MessageQueueFlag;
-using aidl::device::google::atv::audio_proxy::PresentationPosition;
 using android::status_t;
 
 namespace audio_proxy {
@@ -254,4 +253,37 @@
   return ndk::ScopedAStatus::ok();
 }
 
+ndk::ScopedAStatus OutputStreamImpl::getBufferSizeBytes(
+    int64_t* bufferSizeBytes) {
+  *bufferSizeBytes = mStream->getBufferSizeBytes();
+  return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus OutputStreamImpl::getLatencyMs(int32_t* latencyMs) {
+  *latencyMs = mStream->getLatencyMs();
+  return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus OutputStreamImpl::start() {
+  mStream->start();
+  return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus OutputStreamImpl::stop() {
+  mStream->stop();
+  return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus OutputStreamImpl::createMmapBuffer(
+    int32_t minBufferSizeFrames, MmapBufferInfo* info) {
+  *info = mStream->createMmapBuffer(minBufferSizeFrames);
+  return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus OutputStreamImpl::getMmapPosition(
+    PresentationPosition* position) {
+  *position = mStream->getMmapPosition();
+  return ndk::ScopedAStatus::ok();
+}
+
 }  // namespace audio_proxy
\ No newline at end of file
diff --git a/audio_proxy/OutputStreamImpl.h b/audio_proxy/OutputStreamImpl.h
index c783eb0..7a3f7dc 100644
--- a/audio_proxy/OutputStreamImpl.h
+++ b/audio_proxy/OutputStreamImpl.h
@@ -28,6 +28,8 @@
 
 using aidl::device::google::atv::audio_proxy::AudioDrain;
 using aidl::device::google::atv::audio_proxy::BnOutputStream;
+using aidl::device::google::atv::audio_proxy::MmapBufferInfo;
+using aidl::device::google::atv::audio_proxy::PresentationPosition;
 using aidl::device::google::atv::audio_proxy::WriteStatus;
 
 namespace audio_proxy {
@@ -56,6 +58,15 @@
 
   ndk::ScopedAStatus setVolume(float left, float right) override;
 
+  ndk::ScopedAStatus getBufferSizeBytes(int64_t* bufferSizeBytes) override;
+  ndk::ScopedAStatus getLatencyMs(int32_t* latencyMs) override;
+
+  ndk::ScopedAStatus start() override;
+  ndk::ScopedAStatus stop() override;
+  ndk::ScopedAStatus createMmapBuffer(int32_t minBufferSizeFrames,
+                                      MmapBufferInfo* info) override;
+  ndk::ScopedAStatus getMmapPosition(PresentationPosition* position) override;
+
  private:
   typedef void (*EventFlagDeleter)(EventFlag*);
 
diff --git a/audio_proxy/interfaces/aidl/Android.bp b/audio_proxy/interfaces/aidl/Android.bp
index 543cb0d..56ad152 100644
--- a/audio_proxy/interfaces/aidl/Android.bp
+++ b/audio_proxy/interfaces/aidl/Android.bp
@@ -14,6 +14,7 @@
         "android.hardware.common.fmq-V1",
     ],
     stability: "vintf",
+    frozen: true,
     backend: {
         ndk: {
             enabled: true,
@@ -25,5 +26,27 @@
             enabled: false,
         },
     },
-    versions: ["1"],
+    versions_with_info: [
+        {
+            version: "1",
+            imports: [
+                "android.hardware.common-V2",
+                "android.hardware.common.fmq-V1",
+            ],
+        },
+        {
+            version: "2",
+            imports: [
+                "android.hardware.common-V2",
+                "android.hardware.common.fmq-V1",
+            ],
+        },
+        {
+            version: "3",
+            imports: [
+                "android.hardware.common-V2",
+                "android.hardware.common.fmq-V1",
+            ],
+        },
+    ],
 }
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/.hash b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/.hash
new file mode 100644
index 0000000..87f9d36
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/.hash
@@ -0,0 +1 @@
+36478e9608536b679d90121e62177577f2aae0b7
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/AudioChannelMask.aidl b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/AudioChannelMask.aidl
new file mode 100644
index 0000000..821a29a
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/AudioChannelMask.aidl
@@ -0,0 +1,24 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package device.google.atv.audio_proxy;
+@Backing(type="int") @VintfStability
+enum AudioChannelMask {
+  MONO = 1,
+  STEREO = 3,
+}
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/AudioConfig.aidl b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/AudioConfig.aidl
new file mode 100644
index 0000000..b75f906
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/AudioConfig.aidl
@@ -0,0 +1,27 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package device.google.atv.audio_proxy;
+@VintfStability
+parcelable AudioConfig {
+  device.google.atv.audio_proxy.AudioFormat format;
+  int sampleRateHz;
+  device.google.atv.audio_proxy.AudioChannelMask channelMask;
+  long bufferSizeBytes;
+  int latencyMs;
+}
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/AudioDrain.aidl b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/AudioDrain.aidl
new file mode 100644
index 0000000..3b78e78
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/AudioDrain.aidl
@@ -0,0 +1,24 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package device.google.atv.audio_proxy;
+@Backing(type="int") @VintfStability
+enum AudioDrain {
+  ALL = 0,
+  EARLY_NOTIFY = 1,
+}
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/AudioFormat.aidl b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/AudioFormat.aidl
new file mode 100644
index 0000000..778ce15
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/AudioFormat.aidl
@@ -0,0 +1,25 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package device.google.atv.audio_proxy;
+@Backing(type="int") @VintfStability
+enum AudioFormat {
+  PCM_16_BIT = 1,
+  PCM_8_BIT = 2,
+  PCM_FLOAT = 5,
+}
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/AudioOutputFlag.aidl b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/AudioOutputFlag.aidl
new file mode 100644
index 0000000..100762a
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/AudioOutputFlag.aidl
@@ -0,0 +1,25 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package device.google.atv.audio_proxy;
+@Backing(type="int") @VintfStability
+enum AudioOutputFlag {
+  NONE = 0,
+  DIRECT = 1,
+  HW_AV_SYNC = 64,
+}
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/IAudioProxy.aidl b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/IAudioProxy.aidl
new file mode 100644
index 0000000..eadd1a5
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/IAudioProxy.aidl
@@ -0,0 +1,23 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package device.google.atv.audio_proxy;
+@VintfStability
+interface IAudioProxy {
+  void start(in device.google.atv.audio_proxy.IStreamProvider provider);
+}
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/IOutputStream.aidl b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/IOutputStream.aidl
new file mode 100644
index 0000000..44abe39
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/IOutputStream.aidl
@@ -0,0 +1,32 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package device.google.atv.audio_proxy;
+@VintfStability
+interface IOutputStream {
+  void standby();
+  void close();
+  void pause();
+  void resume();
+  void drain(device.google.atv.audio_proxy.AudioDrain drain);
+  void flush();
+  void prepareForWriting(in int frameSize, in int framesCount, out android.hardware.common.fmq.MQDescriptor<byte,android.hardware.common.fmq.SynchronizedReadWrite> dataMQ, out android.hardware.common.fmq.MQDescriptor<device.google.atv.audio_proxy.WriteStatus,android.hardware.common.fmq.SynchronizedReadWrite> statusMQ);
+  void setVolume(float left, float right);
+  long getBufferSizeBytes();
+  int getLatencyMs();
+}
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/IStreamProvider.aidl b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/IStreamProvider.aidl
new file mode 100644
index 0000000..4d05698
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/IStreamProvider.aidl
@@ -0,0 +1,23 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package device.google.atv.audio_proxy;
+@VintfStability
+interface IStreamProvider {
+  device.google.atv.audio_proxy.IOutputStream openOutputStream(in String address, in device.google.atv.audio_proxy.AudioConfig config, in int flags);
+}
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/MessageQueueFlag.aidl b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/MessageQueueFlag.aidl
new file mode 100644
index 0000000..736bfc3
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/MessageQueueFlag.aidl
@@ -0,0 +1,24 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package device.google.atv.audio_proxy;
+@Backing(type="int") @VintfStability
+enum MessageQueueFlag {
+  NOT_EMPTY = 1,
+  NOT_FULL = 2,
+}
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/PresentationPosition.aidl b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/PresentationPosition.aidl
new file mode 100644
index 0000000..4fa7cb9
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/PresentationPosition.aidl
@@ -0,0 +1,24 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package device.google.atv.audio_proxy;
+@FixedSize @VintfStability
+parcelable PresentationPosition {
+  long frames;
+  device.google.atv.audio_proxy.TimeSpec timestamp;
+}
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/TimeSpec.aidl b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/TimeSpec.aidl
new file mode 100644
index 0000000..daed6ac
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/TimeSpec.aidl
@@ -0,0 +1,24 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package device.google.atv.audio_proxy;
+@FixedSize @VintfStability
+parcelable TimeSpec {
+  long tvSec;
+  long tvNSec;
+}
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/WriteStatus.aidl b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/WriteStatus.aidl
new file mode 100644
index 0000000..169163a
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/2/device/google/atv/audio_proxy/WriteStatus.aidl
@@ -0,0 +1,24 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package device.google.atv.audio_proxy;
+@FixedSize @VintfStability
+parcelable WriteStatus {
+  long written;
+  device.google.atv.audio_proxy.PresentationPosition position;
+}
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/.hash b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/.hash
new file mode 100644
index 0000000..a5ef705
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/.hash
@@ -0,0 +1 @@
+70fdeee12fa5bd9b169842e36699920dd0283c56
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/AudioChannelMask.aidl b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/AudioChannelMask.aidl
new file mode 100644
index 0000000..821a29a
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/AudioChannelMask.aidl
@@ -0,0 +1,24 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package device.google.atv.audio_proxy;
+@Backing(type="int") @VintfStability
+enum AudioChannelMask {
+  MONO = 1,
+  STEREO = 3,
+}
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/AudioConfig.aidl b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/AudioConfig.aidl
new file mode 100644
index 0000000..b75f906
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/AudioConfig.aidl
@@ -0,0 +1,27 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package device.google.atv.audio_proxy;
+@VintfStability
+parcelable AudioConfig {
+  device.google.atv.audio_proxy.AudioFormat format;
+  int sampleRateHz;
+  device.google.atv.audio_proxy.AudioChannelMask channelMask;
+  long bufferSizeBytes;
+  int latencyMs;
+}
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/AudioDrain.aidl b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/AudioDrain.aidl
new file mode 100644
index 0000000..3b78e78
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/AudioDrain.aidl
@@ -0,0 +1,24 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package device.google.atv.audio_proxy;
+@Backing(type="int") @VintfStability
+enum AudioDrain {
+  ALL = 0,
+  EARLY_NOTIFY = 1,
+}
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/AudioFormat.aidl b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/AudioFormat.aidl
new file mode 100644
index 0000000..778ce15
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/AudioFormat.aidl
@@ -0,0 +1,25 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package device.google.atv.audio_proxy;
+@Backing(type="int") @VintfStability
+enum AudioFormat {
+  PCM_16_BIT = 1,
+  PCM_8_BIT = 2,
+  PCM_FLOAT = 5,
+}
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/AudioOutputFlag.aidl b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/AudioOutputFlag.aidl
new file mode 100644
index 0000000..100762a
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/AudioOutputFlag.aidl
@@ -0,0 +1,25 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package device.google.atv.audio_proxy;
+@Backing(type="int") @VintfStability
+enum AudioOutputFlag {
+  NONE = 0,
+  DIRECT = 1,
+  HW_AV_SYNC = 64,
+}
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/IAudioProxy.aidl b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/IAudioProxy.aidl
new file mode 100644
index 0000000..eadd1a5
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/IAudioProxy.aidl
@@ -0,0 +1,23 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package device.google.atv.audio_proxy;
+@VintfStability
+interface IAudioProxy {
+  void start(in device.google.atv.audio_proxy.IStreamProvider provider);
+}
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/IOutputStream.aidl b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/IOutputStream.aidl
new file mode 100644
index 0000000..7b5f9ce
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/IOutputStream.aidl
@@ -0,0 +1,36 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package device.google.atv.audio_proxy;
+@VintfStability
+interface IOutputStream {
+  void standby();
+  void close();
+  void pause();
+  void resume();
+  void drain(device.google.atv.audio_proxy.AudioDrain drain);
+  void flush();
+  void prepareForWriting(in int frameSize, in int framesCount, out android.hardware.common.fmq.MQDescriptor<byte,android.hardware.common.fmq.SynchronizedReadWrite> dataMQ, out android.hardware.common.fmq.MQDescriptor<device.google.atv.audio_proxy.WriteStatus,android.hardware.common.fmq.SynchronizedReadWrite> statusMQ);
+  void setVolume(float left, float right);
+  long getBufferSizeBytes();
+  int getLatencyMs();
+  void start();
+  void stop();
+  device.google.atv.audio_proxy.MmapBufferInfo createMmapBuffer(int minSizeFrames);
+  device.google.atv.audio_proxy.PresentationPosition getMmapPosition();
+}
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/IStreamProvider.aidl b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/IStreamProvider.aidl
new file mode 100644
index 0000000..4d05698
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/IStreamProvider.aidl
@@ -0,0 +1,23 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package device.google.atv.audio_proxy;
+@VintfStability
+interface IStreamProvider {
+  device.google.atv.audio_proxy.IOutputStream openOutputStream(in String address, in device.google.atv.audio_proxy.AudioConfig config, in int flags);
+}
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/MessageQueueFlag.aidl b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/MessageQueueFlag.aidl
new file mode 100644
index 0000000..736bfc3
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/MessageQueueFlag.aidl
@@ -0,0 +1,24 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package device.google.atv.audio_proxy;
+@Backing(type="int") @VintfStability
+enum MessageQueueFlag {
+  NOT_EMPTY = 1,
+  NOT_FULL = 2,
+}
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/MmapBufferInfo.aidl b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/MmapBufferInfo.aidl
new file mode 100644
index 0000000..c205c97
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/MmapBufferInfo.aidl
@@ -0,0 +1,26 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package device.google.atv.audio_proxy;
+@VintfStability
+parcelable MmapBufferInfo {
+  ParcelFileDescriptor sharedMemoryFd;
+  int bufferSizeFrames;
+  int burstSizeFrames;
+  int flags;
+}
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/PresentationPosition.aidl b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/PresentationPosition.aidl
new file mode 100644
index 0000000..4fa7cb9
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/PresentationPosition.aidl
@@ -0,0 +1,24 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package device.google.atv.audio_proxy;
+@FixedSize @VintfStability
+parcelable PresentationPosition {
+  long frames;
+  device.google.atv.audio_proxy.TimeSpec timestamp;
+}
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/TimeSpec.aidl b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/TimeSpec.aidl
new file mode 100644
index 0000000..daed6ac
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/TimeSpec.aidl
@@ -0,0 +1,24 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package device.google.atv.audio_proxy;
+@FixedSize @VintfStability
+parcelable TimeSpec {
+  long tvSec;
+  long tvNSec;
+}
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/WriteStatus.aidl b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/WriteStatus.aidl
new file mode 100644
index 0000000..169163a
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/3/device/google/atv/audio_proxy/WriteStatus.aidl
@@ -0,0 +1,24 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package device.google.atv.audio_proxy;
+@FixedSize @VintfStability
+parcelable WriteStatus {
+  long written;
+  device.google.atv.audio_proxy.PresentationPosition position;
+}
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/current/device/google/atv/audio_proxy/AudioConfig.aidl b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/current/device/google/atv/audio_proxy/AudioConfig.aidl
index 2c32b79..b75f906 100644
--- a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/current/device/google/atv/audio_proxy/AudioConfig.aidl
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/current/device/google/atv/audio_proxy/AudioConfig.aidl
@@ -22,4 +22,6 @@
   device.google.atv.audio_proxy.AudioFormat format;
   int sampleRateHz;
   device.google.atv.audio_proxy.AudioChannelMask channelMask;
+  long bufferSizeBytes;
+  int latencyMs;
 }
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/current/device/google/atv/audio_proxy/IOutputStream.aidl b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/current/device/google/atv/audio_proxy/IOutputStream.aidl
index 226ebd4..7b5f9ce 100644
--- a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/current/device/google/atv/audio_proxy/IOutputStream.aidl
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/current/device/google/atv/audio_proxy/IOutputStream.aidl
@@ -27,4 +27,10 @@
   void flush();
   void prepareForWriting(in int frameSize, in int framesCount, out android.hardware.common.fmq.MQDescriptor<byte,android.hardware.common.fmq.SynchronizedReadWrite> dataMQ, out android.hardware.common.fmq.MQDescriptor<device.google.atv.audio_proxy.WriteStatus,android.hardware.common.fmq.SynchronizedReadWrite> statusMQ);
   void setVolume(float left, float right);
+  long getBufferSizeBytes();
+  int getLatencyMs();
+  void start();
+  void stop();
+  device.google.atv.audio_proxy.MmapBufferInfo createMmapBuffer(int minSizeFrames);
+  device.google.atv.audio_proxy.PresentationPosition getMmapPosition();
 }
diff --git a/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/current/device/google/atv/audio_proxy/MmapBufferInfo.aidl b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/current/device/google/atv/audio_proxy/MmapBufferInfo.aidl
new file mode 100644
index 0000000..c205c97
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/aidl_api/device.google.atv.audio_proxy-aidl/current/device/google/atv/audio_proxy/MmapBufferInfo.aidl
@@ -0,0 +1,26 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package device.google.atv.audio_proxy;
+@VintfStability
+parcelable MmapBufferInfo {
+  ParcelFileDescriptor sharedMemoryFd;
+  int bufferSizeFrames;
+  int burstSizeFrames;
+  int flags;
+}
diff --git a/audio_proxy/interfaces/aidl/device/google/atv/audio_proxy/AudioConfig.aidl b/audio_proxy/interfaces/aidl/device/google/atv/audio_proxy/AudioConfig.aidl
index 4e7a85f..97b5e17 100644
--- a/audio_proxy/interfaces/aidl/device/google/atv/audio_proxy/AudioConfig.aidl
+++ b/audio_proxy/interfaces/aidl/device/google/atv/audio_proxy/AudioConfig.aidl
@@ -11,4 +11,10 @@
     AudioFormat format;
     int sampleRateHz;
     AudioChannelMask channelMask;
-}
\ No newline at end of file
+
+    // Expected buffer size and latency for the stream. If 0, the impl should
+    // provide their own value.
+    long bufferSizeBytes;
+    int latencyMs;
+}
+
diff --git a/audio_proxy/interfaces/aidl/device/google/atv/audio_proxy/IOutputStream.aidl b/audio_proxy/interfaces/aidl/device/google/atv/audio_proxy/IOutputStream.aidl
index cffc255..955d369 100644
--- a/audio_proxy/interfaces/aidl/device/google/atv/audio_proxy/IOutputStream.aidl
+++ b/audio_proxy/interfaces/aidl/device/google/atv/audio_proxy/IOutputStream.aidl
@@ -4,6 +4,8 @@
 import android.hardware.common.fmq.SynchronizedReadWrite;
 
 import device.google.atv.audio_proxy.AudioDrain;
+import device.google.atv.audio_proxy.MmapBufferInfo;
+import device.google.atv.audio_proxy.PresentationPosition;
 import device.google.atv.audio_proxy.WriteStatus;
 
 /**
@@ -42,4 +44,26 @@
      * Volume control.
      */
     void setVolume(float left, float right);
+
+    /**
+     * Get the buffer size and latency of the stream. They're called before starting the playback.
+     */
+    long getBufferSizeBytes();
+    int getLatencyMs();
+
+    /**
+     * Start/Stop playback for MMAP_NOIRQ stream.
+     */
+    void start();
+    void stop();
+
+    /**
+     * Create a share memory for MMAP_NOIRQ stream.
+     */
+    MmapBufferInfo createMmapBuffer(int minSizeFrames);
+
+    /**
+     * Query the presentation position for MMAP_NOIRQ stream.
+     */
+    PresentationPosition getMmapPosition();
 }
diff --git a/audio_proxy/interfaces/aidl/device/google/atv/audio_proxy/MmapBufferInfo.aidl b/audio_proxy/interfaces/aidl/device/google/atv/audio_proxy/MmapBufferInfo.aidl
new file mode 100644
index 0000000..dbc7f70
--- /dev/null
+++ b/audio_proxy/interfaces/aidl/device/google/atv/audio_proxy/MmapBufferInfo.aidl
@@ -0,0 +1,15 @@
+package device.google.atv.audio_proxy;
+
+import android.os.ParcelFileDescriptor;
+
+/**
+ * Shared memory and the associated info for the playback.
+ * This is the corresponding structure of audio HAL MmapBufferInfo.
+ */
+@VintfStability
+parcelable MmapBufferInfo {
+    ParcelFileDescriptor sharedMemoryFd;
+    int bufferSizeFrames;
+    int burstSizeFrames;
+    int flags;
+}
diff --git a/audio_proxy/public/audio_proxy.h b/audio_proxy/public/audio_proxy.h
index 097d33b..f6ea632 100644
--- a/audio_proxy/public/audio_proxy.h
+++ b/audio_proxy/public/audio_proxy.h
@@ -46,7 +46,10 @@
   AUDIO_PROXY_FORMAT_INVALID = 0xFFFFFFFFu,
   AUDIO_PROXY_FORMAT_PCM_16_BIT = 0x1u,
   AUDIO_PROXY_FORMAT_PCM_8_BIT = 0x2u,
+  AUDIO_PROXY_FORMAT_PCM_32_BIT = 0x3u,
+  AUDIO_PROXY_FORMAT_PCM_8_24_BIT = 0x4u,
   AUDIO_PROXY_FORMAT_PCM_FLOAT = 0x5u,
+  AUDIO_PROXY_FORMAT_PCM_24_BIT_PACKED = 0x6u,
 };
 typedef uint32_t audio_proxy_format_t;
 
@@ -55,6 +58,35 @@
   AUDIO_PROXY_CHANNEL_INVALID = 0xC0000000u,
   AUDIO_PROXY_CHANNEL_OUT_MONO = 0x1u,
   AUDIO_PROXY_CHANNEL_OUT_STEREO = 0x3u,
+  AUDIO_PROXY_CHANNEL_OUT_2POINT1 = 0xBu,
+  AUDIO_PROXY_CHANNEL_OUT_TRI = 0x7u,
+  AUDIO_PROXY_CHANNEL_OUT_TRI_BACK = 0x103u,
+  AUDIO_PROXY_CHANNEL_OUT_3POINT1 = 0xFu,
+  AUDIO_PROXY_CHANNEL_OUT_2POINT0POINT2 = 0xC0003u,
+  AUDIO_PROXY_CHANNEL_OUT_2POINT1POINT2 = 0xC000Bu,
+  AUDIO_PROXY_CHANNEL_OUT_3POINT0POINT2 = 0xC0007u,
+  AUDIO_PROXY_CHANNEL_OUT_3POINT1POINT2 = 0xC000Fu,
+  AUDIO_PROXY_CHANNEL_OUT_QUAD = 0x33u,
+  // AUDIO_PROXY_CHANNEL_OUT_QUAD_BACK = 0x33u,
+  AUDIO_PROXY_CHANNEL_OUT_QUAD_SIDE = 0x603u,
+  AUDIO_PROXY_CHANNEL_OUT_SURROUND = 0x107u,
+  AUDIO_PROXY_CHANNEL_OUT_PENTA = 0x37u,
+  AUDIO_PROXY_CHANNEL_OUT_5POINT1 = 0x3Fu,
+  // AUDIO_PROXY_CHANNEL_OUT_5POINT1_BACK = 0x3Fu,
+  AUDIO_PROXY_CHANNEL_OUT_5POINT1_SIDE = 0x60Fu,
+  AUDIO_PROXY_CHANNEL_OUT_5POINT1POINT2 = 0xC003Fu,
+  AUDIO_PROXY_CHANNEL_OUT_5POINT1POINT4 = 0x2D03Fu,
+  AUDIO_PROXY_CHANNEL_OUT_6POINT1 = 0x13Fu,
+  AUDIO_PROXY_CHANNEL_OUT_7POINT1 = 0x63Fu,
+  AUDIO_PROXY_CHANNEL_OUT_7POINT1POINT2 = 0xC063Fu,
+  AUDIO_PROXY_CHANNEL_OUT_7POINT1POINT4 = 0x2D63Fu,
+  AUDIO_PROXY_CHANNEL_OUT_13POINT_360RA = 0x72F607u,
+  AUDIO_PROXY_CHANNEL_OUT_22POINT2 = 0xFFFFFFu,
+  AUDIO_PROXY_CHANNEL_OUT_MONO_HAPTIC_A = 0x20000001u,
+  AUDIO_PROXY_CHANNEL_OUT_STEREO_HAPTIC_A = 0x20000003u,
+  AUDIO_PROXY_CHANNEL_OUT_HAPTIC_AB = 0x30000000u,
+  AUDIO_PROXY_CHANNEL_OUT_MONO_HAPTIC_AB = 0x30000001u,
+  AUDIO_PROXY_CHANNEL_OUT_STEREO_HAPTIC_AB = 0x30000003u,
 };
 typedef uint32_t audio_proxy_channel_mask_t;
 
@@ -75,13 +107,21 @@
 
 // AudioConfig
 typedef struct {
+  int64_t buffer_size_bytes;
+  int32_t latency_ms;
+
+  // Points to extra fields defined in the future versions.
+  void* extension;
+} audio_proxy_config_v2_t;
+
+typedef struct {
   uint32_t sample_rate;
   audio_proxy_channel_mask_t channel_mask;
   audio_proxy_format_t format;
   uint32_t frame_count;
 
-  // Points to extra fields defined in the future versions.
-  void* extension;
+  // Points to extra fields.
+  audio_proxy_config_v2_t* v2;
 } audio_proxy_config_t;
 
 // Util structure for key value pair.
@@ -93,10 +133,32 @@
 typedef void (*audio_proxy_get_parameters_callback_t)(
     void*, const audio_proxy_key_val_t*);
 
-// The following struct/functions mirror those definitions in audio HAL. They
-// should have the same requirement as audio HAL interfaces, unless specified.
+enum {
+  AUDIO_PROXY_MMAP_BUFFER_FLAG_NONE = 0x0,
+  AUDIO_PROXY_MMAP_BUFFER_FLAG_APPLICATION_SHAREABLE = 0x1,
+};
+typedef int32_t audio_proxy_mmap_buffer_flag_t;
+
+typedef struct {
+  int shared_memory_fd;
+  int32_t buffer_size_frames;
+  int32_t burst_size_frames;
+  audio_proxy_mmap_buffer_flag_t flags;
+} audio_proxy_mmap_buffer_info_t;
 
 // IStreamOut.
+struct audio_proxy_stream_out_v2 {
+  void (*start)(struct audio_proxy_stream_out_v2* stream);
+  void (*stop)(struct audio_proxy_stream_out_v2* stream);
+  audio_proxy_mmap_buffer_info_t (*create_mmap_buffer)(
+      struct audio_proxy_stream_out_v2* stream, int32_t min_buffer_size_frames);
+  void (*get_mmap_position)(struct audio_proxy_stream_out_v2* stream,
+                            int64_t* frames, struct timespec* timestamp);
+  // Pointer to the next version structure, for compatibility.
+  void* extension;
+};
+typedef struct audio_proxy_stream_out_v2 audio_proxy_stream_out_v2_t;
+
 struct audio_proxy_stream_out {
   size_t (*get_buffer_size)(const struct audio_proxy_stream_out* stream);
   uint64_t (*get_frame_count)(const struct audio_proxy_stream_out* stream);
@@ -191,8 +253,8 @@
   // optional.
   int (*dump)(const struct audio_proxy_stream_out* stream, int fd);
 
-  // Pointer to the next version structure, for compatibility.
-  void* extension;
+  // Pointer to the next version structure.
+  audio_proxy_stream_out_v2_t* v2;
 };
 
 typedef struct audio_proxy_stream_out audio_proxy_stream_out_t;
diff --git a/audio_proxy/sepolicy/OWNERS b/audio_proxy/sepolicy/OWNERS
new file mode 100644
index 0000000..508598e
--- /dev/null
+++ b/audio_proxy/sepolicy/OWNERS
@@ -0,0 +1,2 @@
+include platform/system/sepolicy:/OWNERS
+
diff --git a/audio_proxy/sepolicy/public/hal_audio_proxy.te b/audio_proxy/sepolicy/public/hal_audio_proxy.te
index 2ebdbfe..8b643b9 100644
--- a/audio_proxy/sepolicy/public/hal_audio_proxy.te
+++ b/audio_proxy/sepolicy/public/hal_audio_proxy.te
@@ -4,4 +4,3 @@
 binder_call(hal_audio_proxy_client, hal_audio_proxy_server);
 binder_call(hal_audio_proxy_server, hal_audio_proxy_client);
 binder_call(hal_audio_proxy_server, servicemanager);
-
diff --git a/audio_proxy/sepolicy/vendor/dumpstate.te b/audio_proxy/sepolicy/vendor/dumpstate.te
new file mode 100644
index 0000000..4fc2358
--- /dev/null
+++ b/audio_proxy/sepolicy/vendor/dumpstate.te
@@ -0,0 +1 @@
+binder_call(dumpstate, hal_audio_proxy_default);
diff --git a/audio_proxy/sepolicy/vendor/hal_audio_proxy_default.te b/audio_proxy/sepolicy/vendor/hal_audio_proxy_default.te
index 5534200..e53bfc5 100644
--- a/audio_proxy/sepolicy/vendor/hal_audio_proxy_default.te
+++ b/audio_proxy/sepolicy/vendor/hal_audio_proxy_default.te
@@ -8,16 +8,5 @@
 hal_server_domain(hal_audio_proxy_default, hal_audio);
 hal_server_domain(hal_audio_proxy_default, hal_audio_proxy);
 
-# allows audio proxy service access audio HAL interfaces.
-hal_client_domain(hal_audio_proxy_default, hal_audio);
-
 # allow audioserver to call hal_audio dump with its own fd to retrieve status
 allow hal_audio_proxy_default audioserver:fifo_file write;
-
-allow dumpstate hal_audio_proxy_default:binder call;
-
-type hal_audio_proxy_service, vendor_service, protected_service, service_manager_type;
-hal_attribute_service(hal_audio_proxy, hal_audio_proxy_service);
-
-add_service(hal_audio_proxy_default, hal_audio_proxy_service);
-
diff --git a/audio_proxy/sepolicy/vendor/service.te b/audio_proxy/sepolicy/vendor/service.te
new file mode 100644
index 0000000..f96c68d
--- /dev/null
+++ b/audio_proxy/sepolicy/vendor/service.te
@@ -0,0 +1,2 @@
+type hal_audio_proxy_service, hal_service_type, protected_service, service_manager_type;
+hal_attribute_service(hal_audio_proxy, hal_audio_proxy_service);
diff --git a/audio_proxy/service/AidlTypes.h b/audio_proxy/service/AidlTypes.h
index 5e3fc9b..022098b 100644
--- a/audio_proxy/service/AidlTypes.h
+++ b/audio_proxy/service/AidlTypes.h
@@ -18,6 +18,8 @@
 #include <aidl/device/google/atv/audio_proxy/AudioConfig.h>
 #include <aidl/device/google/atv/audio_proxy/AudioDrain.h>
 #include <aidl/device/google/atv/audio_proxy/AudioFormat.h>
+#include <aidl/device/google/atv/audio_proxy/MmapBufferInfo.h>
+#include <aidl/device/google/atv/audio_proxy/PresentationPosition.h>
 #include <aidl/device/google/atv/audio_proxy/WriteStatus.h>
 
 namespace audio_proxy::service {
@@ -28,6 +30,10 @@
 using AidlAudioConfig = aidl::device::google::atv::audio_proxy::AudioConfig;
 using AidlAudioDrain = aidl::device::google::atv::audio_proxy::AudioDrain;
 using AidlAudioFormat = aidl::device::google::atv::audio_proxy::AudioFormat;
+using AidlMmapBufferInfo =
+    aidl::device::google::atv::audio_proxy::MmapBufferInfo;
+using AidlPresentationPosition =
+    aidl::device::google::atv::audio_proxy::PresentationPosition;
 using AidlWriteStatus = aidl::device::google::atv::audio_proxy::WriteStatus;
 
 }  // namespace audio_proxy::service
\ No newline at end of file
diff --git a/audio_proxy/service/Android.bp b/audio_proxy/service/Android.bp
index 711add8..6dd2cc1 100644
--- a/audio_proxy/service/Android.bp
+++ b/audio_proxy/service/Android.bp
@@ -41,6 +41,7 @@
 
   srcs: [
     "AudioProxyImpl.cpp",
+    "AudioUtil.cpp",
     "BusOutputStream.cpp",
     "BusStreamProvider.cpp",
     "DeviceImpl.cpp",
@@ -53,7 +54,7 @@
   ],
 
   shared_libs: [
-    "device.google.atv.audio_proxy-aidl-V1-ndk",
+    "device.google.atv.audio_proxy-aidl-V3-ndk",
     "libbase",
     "libbinder_ndk",
     "libhidlbase",
diff --git a/audio_proxy/service/AudioUtil.cpp b/audio_proxy/service/AudioUtil.cpp
new file mode 100644
index 0000000..b51c63d
--- /dev/null
+++ b/audio_proxy/service/AudioUtil.cpp
@@ -0,0 +1,38 @@
+// Copyright (C) 2022 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.
+
+#include "AudioUtil.h"
+
+#include <system/audio.h>
+
+namespace audio_proxy::service {
+int computeFrameSize(const AidlAudioConfig& config) {
+  audio_format_t format = static_cast<audio_format_t>(config.format);
+
+  if (!audio_has_proportional_frames(format)) {
+    return sizeof(int8_t);
+  }
+
+  size_t channelSampleSize = audio_bytes_per_sample(format);
+  return audio_channel_count_from_out_mask(
+             static_cast<audio_channel_mask_t>(config.channelMask)) *
+         channelSampleSize;
+}
+
+int64_t computeBufferSizeBytes(const AidlAudioConfig& config,
+                               int32_t bufferSizeMs) {
+  return static_cast<int64_t>(bufferSizeMs) * config.sampleRateHz *
+         computeFrameSize(config) / 1000;
+}
+}  // namespace audio_proxy::service
\ No newline at end of file
diff --git a/audio_proxy/service/AudioUtil.h b/audio_proxy/service/AudioUtil.h
new file mode 100644
index 0000000..5430472
--- /dev/null
+++ b/audio_proxy/service/AudioUtil.h
@@ -0,0 +1,25 @@
+// Copyright (C) 2022 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.
+
+#pragma once
+
+#include <cstdint>
+
+#include "AidlTypes.h"
+
+namespace audio_proxy::service {
+int computeFrameSize(const AidlAudioConfig& config);
+int64_t computeBufferSizeBytes(const AidlAudioConfig& config,
+                               int32_t bufferSizeMs);
+}  // namespace audio_proxy::service
\ No newline at end of file
diff --git a/audio_proxy/service/BusOutputStream.cpp b/audio_proxy/service/BusOutputStream.cpp
index 40ac8d4..e456faf 100644
--- a/audio_proxy/service/BusOutputStream.cpp
+++ b/audio_proxy/service/BusOutputStream.cpp
@@ -15,7 +15,8 @@
 #include "BusOutputStream.h"
 
 #include <android-base/logging.h>
-#include <system/audio.h>
+
+#include "AudioUtil.h"
 
 namespace audio_proxy::service {
 
@@ -28,18 +29,7 @@
 const AidlAudioConfig& BusOutputStream::getConfig() const { return mConfig; }
 int32_t BusOutputStream::getFlags() const { return mFlags; }
 
-int BusOutputStream::getFrameSize() const {
-  audio_format_t format = static_cast<audio_format_t>(mConfig.format);
-
-  if (!audio_has_proportional_frames(format)) {
-    return sizeof(int8_t);
-  }
-
-  size_t channelSampleSize = audio_bytes_per_sample(format);
-  return audio_channel_count_from_out_mask(
-             static_cast<audio_channel_mask_t>(mConfig.channelMask)) *
-         channelSampleSize;
-}
+int BusOutputStream::getFrameSize() const { return computeFrameSize(mConfig); }
 
 bool BusOutputStream::prepareForWriting(uint32_t frameSize,
                                         uint32_t frameCount) {
diff --git a/audio_proxy/service/BusOutputStream.h b/audio_proxy/service/BusOutputStream.h
index f9fb930..2116bca 100644
--- a/audio_proxy/service/BusOutputStream.h
+++ b/audio_proxy/service/BusOutputStream.h
@@ -50,6 +50,11 @@
                                           const uint8_t* secondMem,
                                           size_t secondLength) = 0;
 
+  virtual bool start() = 0;
+  virtual bool stop() = 0;
+  virtual AidlMmapBufferInfo createMmapBuffer(int32_t minBufferSizeFrames) = 0;
+  virtual AidlPresentationPosition getMmapPosition() = 0;
+
  protected:
   virtual bool prepareForWritingImpl(uint32_t frameSize,
                                      uint32_t frameCount) = 0;
diff --git a/audio_proxy/service/BusStreamProvider.cpp b/audio_proxy/service/BusStreamProvider.cpp
index 42e6dda..af2c5b8 100644
--- a/audio_proxy/service/BusStreamProvider.cpp
+++ b/audio_proxy/service/BusStreamProvider.cpp
@@ -34,9 +34,11 @@
   for (auto& weakStream : mStreamOutList) {
     if (sp<StreamOutImpl> stream = weakStream.promote()) {
       auto oldOutputStream = stream->getOutputStream();
-      auto outputStream = openOutputStream_Locked(oldOutputStream->getAddress(),
-                                                  oldOutputStream->getConfig(),
-                                                  oldOutputStream->getFlags());
+      auto outputStream = openOutputStream_Locked(
+          oldOutputStream->getAddress(), oldOutputStream->getConfig(),
+          oldOutputStream->getFlags(),
+          oldOutputStream->getConfig().bufferSizeBytes,
+          oldOutputStream->getConfig().latencyMs);
       stream->updateOutputStream(std::move(outputStream));
     }
   }
@@ -48,9 +50,11 @@
 }
 
 std::shared_ptr<BusOutputStream> BusStreamProvider::openOutputStream(
-    const std::string& address, const AidlAudioConfig& config, int32_t flags) {
+    const std::string& address, const AidlAudioConfig& config, int32_t flags,
+    int64_t bufferSizeBytes, int32_t latencyMs) {
   std::lock_guard<std::mutex> lock(mLock);
-  return openOutputStream_Locked(address, config, flags);
+  return openOutputStream_Locked(address, config, flags, bufferSizeBytes,
+                                 latencyMs);
 }
 
 void BusStreamProvider::onStreamOutCreated(wp<StreamOutImpl> stream) {
@@ -60,9 +64,14 @@
 }
 
 std::shared_ptr<BusOutputStream> BusStreamProvider::openOutputStream_Locked(
-    const std::string& address, const AidlAudioConfig& config, int32_t flags) {
+    const std::string& address, const AidlAudioConfig& config, int32_t flags,
+    int64_t bufferSizeBytes, int32_t latencyMs) {
+  AidlAudioConfig newConfig = config;
+  newConfig.bufferSizeBytes = bufferSizeBytes;
+  newConfig.latencyMs = latencyMs;
+
   if (!mStreamProvider) {
-    return std::make_shared<DummyBusOutputStream>(address, config, flags);
+    return std::make_shared<DummyBusOutputStream>(address, newConfig, flags);
   }
 
   std::shared_ptr<IOutputStream> stream;
@@ -70,11 +79,22 @@
       mStreamProvider->openOutputStream(address, config, flags, &stream);
   if (!status.isOk() || !stream) {
     LOG(ERROR) << "Failed to open output stream, status " << status.getStatus();
-    return std::make_shared<DummyBusOutputStream>(address, config, flags);
+    return std::make_shared<DummyBusOutputStream>(address, newConfig, flags);
+  }
+
+  int64_t aidlBufferSizeInBytes = -1;
+  if (stream->getBufferSizeBytes(&aidlBufferSizeInBytes).isOk() &&
+      aidlBufferSizeInBytes > 0) {
+    newConfig.bufferSizeBytes = aidlBufferSizeInBytes;
+  }
+
+  int32_t aidlLatencyMs = -1;
+  if (stream->getLatencyMs(&aidlLatencyMs).isOk() && aidlLatencyMs > 0) {
+    newConfig.latencyMs = aidlLatencyMs;
   }
 
   return std::make_shared<RemoteBusOutputStream>(std::move(stream), address,
-                                                 config, flags);
+                                                 newConfig, flags);
 }
 
 size_t BusStreamProvider::cleanAndCountStreamOuts() {
diff --git a/audio_proxy/service/BusStreamProvider.h b/audio_proxy/service/BusStreamProvider.h
index abcca8b..8d3ebbb 100644
--- a/audio_proxy/service/BusStreamProvider.h
+++ b/audio_proxy/service/BusStreamProvider.h
@@ -52,15 +52,16 @@
   // 2. Returns DummyBusOutputStream otherwise.
   // This function always return a non null BusOutputStream.
   std::shared_ptr<BusOutputStream> openOutputStream(
-      const std::string& address, const AidlAudioConfig& config, int32_t flags);
+      const std::string& address, const AidlAudioConfig& config, int32_t flags,
+      int64_t bufferSizeBytes, int32_t latencyMs);
 
   // Clear closed StreamOut and return number of opened StreamOut.
   size_t cleanAndCountStreamOuts();
 
  private:
   std::shared_ptr<BusOutputStream> openOutputStream_Locked(
-      const std::string& address, const AidlAudioConfig& config, int32_t flags)
-      REQUIRES(mLock);
+      const std::string& address, const AidlAudioConfig& config, int32_t flags,
+      int64_t bufferSizeBytes, int32_t latencyMs) REQUIRES(mLock);
 
   // Remove the dead dead from mStreamOutList.
   void cleanStreamOutList_Locked() REQUIRES(mLock);
diff --git a/audio_proxy/service/DeviceImpl.cpp b/audio_proxy/service/DeviceImpl.cpp
index 15637d5..1cbae5a 100644
--- a/audio_proxy/service/DeviceImpl.cpp
+++ b/audio_proxy/service/DeviceImpl.cpp
@@ -22,6 +22,7 @@
 #include <optional>
 
 #include "AidlTypes.h"
+#include "AudioUtil.h"
 #include "BusOutputStream.h"
 #include "BusStreamProvider.h"
 #include "ServiceConfig.h"
@@ -54,7 +55,9 @@
   AidlAudioConfig aidlConfig = {
       .format = static_cast<AidlAudioFormat>(format),
       .sampleRateHz = static_cast<int32_t>(hidl_config.sampleRateHz),
-      .channelMask = static_cast<AidlAudioChannelMask>(channelMask)};
+      .channelMask = static_cast<AidlAudioChannelMask>(channelMask),
+      .bufferSizeBytes = 0,
+      .latencyMs = 0};
 
   return aidlConfig;
 }
@@ -183,8 +186,9 @@
   AidlAudioConfig aidlConfig = {
       .format = static_cast<AidlAudioFormat>(hidl_config.format),
       .sampleRateHz = static_cast<int32_t>(hidl_config.sampleRateHz),
-      .channelMask =
-          static_cast<AidlAudioChannelMask>(hidl_config.channelMask)};
+      .channelMask = static_cast<AidlAudioChannelMask>(hidl_config.channelMask),
+      .bufferSizeBytes = 0,
+      .latencyMs = 0};
 
   return aidlConfig;
 }
@@ -278,11 +282,13 @@
   }
 
   std::shared_ptr<BusOutputStream> busOutputStream =
-      mBusStreamProvider.openOutputStream(address, *aidlConfig, *outputFlags);
+      mBusStreamProvider.openOutputStream(
+          address, *aidlConfig, *outputFlags,
+          computeBufferSizeBytes(*aidlConfig, configIt->second.bufferSizeMs),
+          configIt->second.latencyMs);
   DCHECK(busOutputStream);
-  auto streamOut = sp<StreamOutImpl>::make(
-      std::move(busOutputStream), config.base, configIt->second.bufferSizeMs,
-      configIt->second.latencyMs);
+  auto streamOut =
+      sp<StreamOutImpl>::make(std::move(busOutputStream), config.base);
   mBusStreamProvider.onStreamOutCreated(streamOut);
   _hidl_cb(Result::OK, streamOut, config);
   return Void();
@@ -330,14 +336,14 @@
     return Void();
   }
 
+  auto aidlConfig = toAidlAudioConfig(config);
   std::shared_ptr<BusOutputStream> busOutputStream =
-      mBusStreamProvider.openOutputStream(address,
-                                          toAidlAudioConfig(config),
-                                          static_cast<int32_t>(flags));
+      mBusStreamProvider.openOutputStream(
+          address, aidlConfig, static_cast<int32_t>(flags),
+          computeBufferSizeBytes(aidlConfig, configIt->second.bufferSizeMs),
+          configIt->second.latencyMs);
   DCHECK(busOutputStream);
-  auto streamOut = sp<StreamOutImpl>::make(std::move(busOutputStream), config,
-                                           configIt->second.bufferSizeMs,
-                                           configIt->second.latencyMs);
+  auto streamOut = sp<StreamOutImpl>::make(std::move(busOutputStream), config);
   mBusStreamProvider.onStreamOutCreated(streamOut);
   _hidl_cb(Result::OK, streamOut, config);
   return Void();
diff --git a/audio_proxy/service/DummyBusOutputStream.cpp b/audio_proxy/service/DummyBusOutputStream.cpp
index b18728f..3b9b56e 100644
--- a/audio_proxy/service/DummyBusOutputStream.cpp
+++ b/audio_proxy/service/DummyBusOutputStream.cpp
@@ -115,4 +115,17 @@
   return true;
 }
 
+bool DummyBusOutputStream::start() { return false; }
+
+bool DummyBusOutputStream::stop() { return false; };
+
+AidlMmapBufferInfo DummyBusOutputStream::createMmapBuffer(
+    int32_t minBufferSizeFrames) {
+  return AidlMmapBufferInfo();
+}
+
+AidlPresentationPosition DummyBusOutputStream::getMmapPosition() {
+  return AidlPresentationPosition();
+}
+
 }  // namespace audio_proxy::service
diff --git a/audio_proxy/service/DummyBusOutputStream.h b/audio_proxy/service/DummyBusOutputStream.h
index c5ca47e..0cb8e44 100644
--- a/audio_proxy/service/DummyBusOutputStream.h
+++ b/audio_proxy/service/DummyBusOutputStream.h
@@ -41,6 +41,11 @@
                                   const uint8_t* secondMem,
                                   size_t secondLength) override;
 
+  bool start() override;
+  bool stop() override;
+  AidlMmapBufferInfo createMmapBuffer(int32_t minBufferSizeFrames) override;
+  AidlPresentationPosition getMmapPosition() override;
+
  protected:
   bool prepareForWritingImpl(uint32_t frameSize, uint32_t frameCount) override;
 
diff --git a/audio_proxy/service/RemoteBusOutputStream.cpp b/audio_proxy/service/RemoteBusOutputStream.cpp
index a5edc76..177cb55 100644
--- a/audio_proxy/service/RemoteBusOutputStream.cpp
+++ b/audio_proxy/service/RemoteBusOutputStream.cpp
@@ -158,5 +158,22 @@
   return true;
 }
 
+bool RemoteBusOutputStream::start() { return mStream->start().isOk(); }
+
+bool RemoteBusOutputStream::stop() { return mStream->stop().isOk(); };
+
+AidlMmapBufferInfo RemoteBusOutputStream::createMmapBuffer(
+    int32_t minBufferSizeFrames) {
+  AidlMmapBufferInfo info;
+  mStream->createMmapBuffer(minBufferSizeFrames, &info);
+  return info;
+}
+
+AidlPresentationPosition RemoteBusOutputStream::getMmapPosition() {
+  AidlPresentationPosition position;
+  mStream->getMmapPosition(&position);
+  return position;
+}
+
 }  // namespace service
 }  // namespace audio_proxy
\ No newline at end of file
diff --git a/audio_proxy/service/RemoteBusOutputStream.h b/audio_proxy/service/RemoteBusOutputStream.h
index 6f663cb..cb7de19 100644
--- a/audio_proxy/service/RemoteBusOutputStream.h
+++ b/audio_proxy/service/RemoteBusOutputStream.h
@@ -49,6 +49,11 @@
                                   const uint8_t* secondMem,
                                   size_t secondLength) override;
 
+  bool start() override;
+  bool stop() override;
+  AidlMmapBufferInfo createMmapBuffer(int32_t minBufferSizeFrames) override;
+  AidlPresentationPosition getMmapPosition() override;
+
  protected:
   bool prepareForWritingImpl(uint32_t frameSize, uint32_t frameCount) override;
 
diff --git a/audio_proxy/service/StreamOutImpl.cpp b/audio_proxy/service/StreamOutImpl.cpp
index baf086f..5e4a772 100644
--- a/audio_proxy/service/StreamOutImpl.cpp
+++ b/audio_proxy/service/StreamOutImpl.cpp
@@ -28,6 +28,7 @@
 #include "WriteThread.h"
 
 using android::status_t;
+using android::hardware::hidl_memory;
 
 namespace audio_proxy::service {
 
@@ -73,12 +74,11 @@
 }  // namespace
 
 StreamOutImpl::StreamOutImpl(std::shared_ptr<BusOutputStream> stream,
-                             const StreamOutConfig& config,
-                             uint32_t bufferSizeMs, uint32_t latencyMs)
+                             const StreamOutConfig& config)
     : mStream(std::move(stream)),
       mConfig(config),
-      mBufferSizeMs(bufferSizeMs),
-      mLatencyMs(latencyMs),
+      mBufferSizeBytes(mStream->getConfig().bufferSizeBytes),
+      mLatencyMs(mStream->getConfig().latencyMs),
       mEventFlag(nullptr, deleteEventFlag) {}
 
 StreamOutImpl::~StreamOutImpl() {
@@ -98,12 +98,10 @@
 }
 
 Return<uint64_t> StreamOutImpl::getFrameCount() {
-  return mBufferSizeMs * mConfig.sampleRateHz / 1000;
+  return mBufferSizeBytes / mStream->getFrameSize();
 }
 
-Return<uint64_t> StreamOutImpl::getBufferSize() {
-  return mBufferSizeMs * mConfig.sampleRateHz * mStream->getFrameSize() / 1000;
-}
+Return<uint64_t> StreamOutImpl::getBufferSize() { return mBufferSizeBytes; }
 
 #if MAJOR_VERSION >= 7
 Return<void> StreamOutImpl::getSupportedProfiles(
@@ -441,18 +439,51 @@
   return Void();
 }
 
-Return<Result> StreamOutImpl::start() { return Result::NOT_SUPPORTED; }
+Return<Result> StreamOutImpl::start() {
+  return mStream->start() ? Result::OK : Result::NOT_SUPPORTED;
+}
 
-Return<Result> StreamOutImpl::stop() { return Result::NOT_SUPPORTED; }
+Return<Result> StreamOutImpl::stop() {
+  return mStream->stop() ? Result::OK : Result::NOT_SUPPORTED;
+}
 
 Return<void> StreamOutImpl::createMmapBuffer(int32_t minSizeFrames,
                                              createMmapBuffer_cb _hidl_cb) {
-  _hidl_cb(Result::NOT_SUPPORTED, MmapBufferInfo());
+  MmapBufferInfo hidlInfo;
+  AidlMmapBufferInfo info = mStream->createMmapBuffer(minSizeFrames);
+  int sharedMemoryFd = info.sharedMemoryFd.get();
+  if (sharedMemoryFd == -1) {
+    _hidl_cb(Result::NOT_SUPPORTED, hidlInfo);
+    return Void();
+  }
+
+  native_handle_t* hidlHandle = nullptr;
+  hidlHandle = native_handle_create(1, 0);
+  hidlHandle->data[0] = sharedMemoryFd;
+
+  hidlInfo.sharedMemory =
+      hidl_memory("audio_proxy_mmap_buffer", hidlHandle,
+                  mStream->getFrameSize() * info.bufferSizeFrames);
+  hidlInfo.bufferSizeFrames = info.bufferSizeFrames;
+  hidlInfo.burstSizeFrames = info.burstSizeFrames;
+  hidlInfo.flags = static_cast<hidl_bitfield<MmapBufferFlag>>(info.flags);
+  _hidl_cb(Result::OK, hidlInfo);
   return Void();
 }
 
 Return<void> StreamOutImpl::getMmapPosition(getMmapPosition_cb _hidl_cb) {
-  _hidl_cb(Result::NOT_SUPPORTED, MmapPosition());
+  MmapPosition hidlPosition;
+
+  AidlPresentationPosition position = mStream->getMmapPosition();
+  if (position.timestamp.tvSec == 0 && position.timestamp.tvNSec == 0) {
+    _hidl_cb(Result::NOT_SUPPORTED, hidlPosition);
+    return Void();
+  }
+
+  hidlPosition.timeNanoseconds =
+      position.timestamp.tvSec * kOneSecInNs + position.timestamp.tvNSec;
+  hidlPosition.positionFrames = position.frames;
+  _hidl_cb(Result::OK, hidlPosition);
   return Void();
 }
 
@@ -505,6 +536,10 @@
   }
 
   auto [frames, timestamp] = mWriteThread->getPresentationPosition();
+  if (frames == 0) {
+    return 0;
+  }
+
   return frames + estimatePlayedFramesSince(timestamp, mConfig.sampleRateHz);
 }
 
diff --git a/audio_proxy/service/StreamOutImpl.h b/audio_proxy/service/StreamOutImpl.h
index 80884d9..c1b557d 100644
--- a/audio_proxy/service/StreamOutImpl.h
+++ b/audio_proxy/service/StreamOutImpl.h
@@ -61,8 +61,7 @@
 #endif
 
   StreamOutImpl(std::shared_ptr<BusOutputStream> stream,
-                const StreamOutConfig& config, uint32_t bufferSizeMs,
-                uint32_t latencyMs);
+                const StreamOutConfig& config);
   ~StreamOutImpl() override;
 
   std::shared_ptr<BusOutputStream> getOutputStream();
@@ -171,7 +170,7 @@
   std::shared_ptr<BusOutputStream> mStream;
   const StreamOutConfig mConfig;
 
-  const uint32_t mBufferSizeMs;
+  const uint64_t mBufferSizeBytes;
   const uint32_t mLatencyMs;
 
   std::unique_ptr<CommandMQ> mCommandMQ;
diff --git a/audio_proxy/service/audio_proxy_policy_configuration.xml b/audio_proxy/service/audio_proxy_policy_configuration.xml
index b44ce98..6fddee2 100644
--- a/audio_proxy/service/audio_proxy_policy_configuration.xml
+++ b/audio_proxy/service/audio_proxy_policy_configuration.xml
@@ -10,22 +10,22 @@
              flags="AUDIO_OUTPUT_FLAG_DIRECT" maxOpenCount="0">
       <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
                samplingRates="16000 22050 44100 48000 96000"
-               channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE AUDIO_CHANNEL_OUT_5POINT1POINT2 AUDIO_CHANNEL_OUT_5POINT1POINT4 AUDIO_CHANNEL_OUT_6POINT1 AUDIO_CHANNEL_OUT_7POINT1 AUDIO_CHANNEL_OUT_7POINT1POINT2 AUDIO_CHANNEL_OUT_7POINT1POINT4 AUDIO_CHANNEL_OUT_13POINT_360RA AUDIO_CHANNEL_OUT_22POINT2 AUDIO_CHANNEL_OUT_MONO_HAPTIC_A AUDIO_CHANNEL_OUT_STEREO_HAPTIC_A AUDIO_CHANNEL_OUT_HAPTIC_AB AUDIO_CHANNEL_OUT_MONO_HAPTIC_AB AUDIO_CHANNEL_OUT_STEREO_HAPTIC_AB"/>
+               channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE"/>
       <profile name="" format="AUDIO_FORMAT_PCM_8_BIT"
                samplingRates="16000 22050 44100 48000 96000"
-               channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE AUDIO_CHANNEL_OUT_5POINT1POINT2 AUDIO_CHANNEL_OUT_5POINT1POINT4 AUDIO_CHANNEL_OUT_6POINT1 AUDIO_CHANNEL_OUT_7POINT1 AUDIO_CHANNEL_OUT_7POINT1POINT2 AUDIO_CHANNEL_OUT_7POINT1POINT4 AUDIO_CHANNEL_OUT_13POINT_360RA AUDIO_CHANNEL_OUT_22POINT2 AUDIO_CHANNEL_OUT_MONO_HAPTIC_A AUDIO_CHANNEL_OUT_STEREO_HAPTIC_A AUDIO_CHANNEL_OUT_HAPTIC_AB AUDIO_CHANNEL_OUT_MONO_HAPTIC_AB AUDIO_CHANNEL_OUT_STEREO_HAPTIC_AB"/>
+               channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE"/>
       <profile name="" format="AUDIO_FORMAT_PCM_32_BIT"
                samplingRates="16000 22050 44100 48000 96000"
-               channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE AUDIO_CHANNEL_OUT_5POINT1POINT2 AUDIO_CHANNEL_OUT_5POINT1POINT4 AUDIO_CHANNEL_OUT_6POINT1 AUDIO_CHANNEL_OUT_7POINT1 AUDIO_CHANNEL_OUT_7POINT1POINT2 AUDIO_CHANNEL_OUT_7POINT1POINT4 AUDIO_CHANNEL_OUT_13POINT_360RA AUDIO_CHANNEL_OUT_22POINT2 AUDIO_CHANNEL_OUT_MONO_HAPTIC_A AUDIO_CHANNEL_OUT_STEREO_HAPTIC_A AUDIO_CHANNEL_OUT_HAPTIC_AB AUDIO_CHANNEL_OUT_MONO_HAPTIC_AB AUDIO_CHANNEL_OUT_STEREO_HAPTIC_AB"/>
+               channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE"/>
       <profile name="" format="AUDIO_FORMAT_PCM_8_24_BIT"
                samplingRates="16000 22050 44100 48000 96000"
-               channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE AUDIO_CHANNEL_OUT_5POINT1POINT2 AUDIO_CHANNEL_OUT_5POINT1POINT4 AUDIO_CHANNEL_OUT_6POINT1 AUDIO_CHANNEL_OUT_7POINT1 AUDIO_CHANNEL_OUT_7POINT1POINT2 AUDIO_CHANNEL_OUT_7POINT1POINT4 AUDIO_CHANNEL_OUT_13POINT_360RA AUDIO_CHANNEL_OUT_22POINT2 AUDIO_CHANNEL_OUT_MONO_HAPTIC_A AUDIO_CHANNEL_OUT_STEREO_HAPTIC_A AUDIO_CHANNEL_OUT_HAPTIC_AB AUDIO_CHANNEL_OUT_MONO_HAPTIC_AB AUDIO_CHANNEL_OUT_STEREO_HAPTIC_AB"/>
+               channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE"/>
       <profile name="" format="AUDIO_FORMAT_PCM_FLOAT"
                samplingRates="16000 22050 44100 48000 96000"
-               channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE AUDIO_CHANNEL_OUT_5POINT1POINT2 AUDIO_CHANNEL_OUT_5POINT1POINT4 AUDIO_CHANNEL_OUT_6POINT1 AUDIO_CHANNEL_OUT_7POINT1 AUDIO_CHANNEL_OUT_7POINT1POINT2 AUDIO_CHANNEL_OUT_7POINT1POINT4 AUDIO_CHANNEL_OUT_13POINT_360RA AUDIO_CHANNEL_OUT_22POINT2 AUDIO_CHANNEL_OUT_MONO_HAPTIC_A AUDIO_CHANNEL_OUT_STEREO_HAPTIC_A AUDIO_CHANNEL_OUT_HAPTIC_AB AUDIO_CHANNEL_OUT_MONO_HAPTIC_AB AUDIO_CHANNEL_OUT_STEREO_HAPTIC_AB"/>
+               channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE"/>
       <profile name="" format="AUDIO_FORMAT_PCM_24_BIT_PACKED"
                samplingRates="16000 22050 44100 48000 96000"
-               channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE AUDIO_CHANNEL_OUT_5POINT1POINT2 AUDIO_CHANNEL_OUT_5POINT1POINT4 AUDIO_CHANNEL_OUT_6POINT1 AUDIO_CHANNEL_OUT_7POINT1 AUDIO_CHANNEL_OUT_7POINT1POINT2 AUDIO_CHANNEL_OUT_7POINT1POINT4 AUDIO_CHANNEL_OUT_13POINT_360RA AUDIO_CHANNEL_OUT_22POINT2 AUDIO_CHANNEL_OUT_MONO_HAPTIC_A AUDIO_CHANNEL_OUT_STEREO_HAPTIC_A AUDIO_CHANNEL_OUT_HAPTIC_AB AUDIO_CHANNEL_OUT_MONO_HAPTIC_AB AUDIO_CHANNEL_OUT_STEREO_HAPTIC_AB"/>
+               channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE"/>
     </mixPort>
 
     <mixPort name="mixer_mix_port" role="source">
diff --git a/audio_proxy/service/audio_proxy_policy_configuration_hw_av_sync.xml b/audio_proxy/service/audio_proxy_policy_configuration_hw_av_sync.xml
index e73a6e7..b86a897 100644
--- a/audio_proxy/service/audio_proxy_policy_configuration_hw_av_sync.xml
+++ b/audio_proxy/service/audio_proxy_policy_configuration_hw_av_sync.xml
@@ -9,45 +9,52 @@
     <mixPort name="direct_mix_port" role="source"
              flags="AUDIO_OUTPUT_FLAG_DIRECT" maxOpenCount="0">
       <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
-               samplingRates="16000 22050 44100 48000 96000"
-               channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE AUDIO_CHANNEL_OUT_5POINT1POINT2 AUDIO_CHANNEL_OUT_5POINT1POINT4 AUDIO_CHANNEL_OUT_6POINT1 AUDIO_CHANNEL_OUT_7POINT1 AUDIO_CHANNEL_OUT_7POINT1POINT2 AUDIO_CHANNEL_OUT_7POINT1POINT4 AUDIO_CHANNEL_OUT_13POINT_360RA AUDIO_CHANNEL_OUT_22POINT2 AUDIO_CHANNEL_OUT_MONO_HAPTIC_A AUDIO_CHANNEL_OUT_STEREO_HAPTIC_A AUDIO_CHANNEL_OUT_HAPTIC_AB AUDIO_CHANNEL_OUT_MONO_HAPTIC_AB AUDIO_CHANNEL_OUT_STEREO_HAPTIC_AB"/>
+               samplingRates="16000 22050 24000 32000 44100 48000 96000"
+               channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE"/>
       <profile name="" format="AUDIO_FORMAT_PCM_8_BIT"
-               samplingRates="16000 22050 44100 48000 96000"
-               channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE AUDIO_CHANNEL_OUT_5POINT1POINT2 AUDIO_CHANNEL_OUT_5POINT1POINT4 AUDIO_CHANNEL_OUT_6POINT1 AUDIO_CHANNEL_OUT_7POINT1 AUDIO_CHANNEL_OUT_7POINT1POINT2 AUDIO_CHANNEL_OUT_7POINT1POINT4 AUDIO_CHANNEL_OUT_13POINT_360RA AUDIO_CHANNEL_OUT_22POINT2 AUDIO_CHANNEL_OUT_MONO_HAPTIC_A AUDIO_CHANNEL_OUT_STEREO_HAPTIC_A AUDIO_CHANNEL_OUT_HAPTIC_AB AUDIO_CHANNEL_OUT_MONO_HAPTIC_AB AUDIO_CHANNEL_OUT_STEREO_HAPTIC_AB"/>
+               samplingRates="16000 22050 24000 32000 44100 48000 96000"
+               channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE"/>
       <profile name="" format="AUDIO_FORMAT_PCM_32_BIT"
-               samplingRates="16000 22050 44100 48000 96000"
-               channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE AUDIO_CHANNEL_OUT_5POINT1POINT2 AUDIO_CHANNEL_OUT_5POINT1POINT4 AUDIO_CHANNEL_OUT_6POINT1 AUDIO_CHANNEL_OUT_7POINT1 AUDIO_CHANNEL_OUT_7POINT1POINT2 AUDIO_CHANNEL_OUT_7POINT1POINT4 AUDIO_CHANNEL_OUT_13POINT_360RA AUDIO_CHANNEL_OUT_22POINT2 AUDIO_CHANNEL_OUT_MONO_HAPTIC_A AUDIO_CHANNEL_OUT_STEREO_HAPTIC_A AUDIO_CHANNEL_OUT_HAPTIC_AB AUDIO_CHANNEL_OUT_MONO_HAPTIC_AB AUDIO_CHANNEL_OUT_STEREO_HAPTIC_AB"/>
+               samplingRates="16000 22050 24000 32000 44100 48000 96000"
+               channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE"/>
       <profile name="" format="AUDIO_FORMAT_PCM_8_24_BIT"
-               samplingRates="16000 22050 44100 48000 96000"
-               channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE AUDIO_CHANNEL_OUT_5POINT1POINT2 AUDIO_CHANNEL_OUT_5POINT1POINT4 AUDIO_CHANNEL_OUT_6POINT1 AUDIO_CHANNEL_OUT_7POINT1 AUDIO_CHANNEL_OUT_7POINT1POINT2 AUDIO_CHANNEL_OUT_7POINT1POINT4 AUDIO_CHANNEL_OUT_13POINT_360RA AUDIO_CHANNEL_OUT_22POINT2 AUDIO_CHANNEL_OUT_MONO_HAPTIC_A AUDIO_CHANNEL_OUT_STEREO_HAPTIC_A AUDIO_CHANNEL_OUT_HAPTIC_AB AUDIO_CHANNEL_OUT_MONO_HAPTIC_AB AUDIO_CHANNEL_OUT_STEREO_HAPTIC_AB"/>
+               samplingRates="16000 22050 24000 32000 44100 48000 96000"
+               channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE"/>
       <profile name="" format="AUDIO_FORMAT_PCM_FLOAT"
-               samplingRates="16000 22050 44100 48000 96000"
-               channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE AUDIO_CHANNEL_OUT_5POINT1POINT2 AUDIO_CHANNEL_OUT_5POINT1POINT4 AUDIO_CHANNEL_OUT_6POINT1 AUDIO_CHANNEL_OUT_7POINT1 AUDIO_CHANNEL_OUT_7POINT1POINT2 AUDIO_CHANNEL_OUT_7POINT1POINT4 AUDIO_CHANNEL_OUT_13POINT_360RA AUDIO_CHANNEL_OUT_22POINT2 AUDIO_CHANNEL_OUT_MONO_HAPTIC_A AUDIO_CHANNEL_OUT_STEREO_HAPTIC_A AUDIO_CHANNEL_OUT_HAPTIC_AB AUDIO_CHANNEL_OUT_MONO_HAPTIC_AB AUDIO_CHANNEL_OUT_STEREO_HAPTIC_AB"/>
+               samplingRates="16000 22050 24000 32000 44100 48000 96000"
+               channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE"/>
       <profile name="" format="AUDIO_FORMAT_PCM_24_BIT_PACKED"
-               samplingRates="16000 22050 44100 48000 96000"
-               channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE AUDIO_CHANNEL_OUT_5POINT1POINT2 AUDIO_CHANNEL_OUT_5POINT1POINT4 AUDIO_CHANNEL_OUT_6POINT1 AUDIO_CHANNEL_OUT_7POINT1 AUDIO_CHANNEL_OUT_7POINT1POINT2 AUDIO_CHANNEL_OUT_7POINT1POINT4 AUDIO_CHANNEL_OUT_13POINT_360RA AUDIO_CHANNEL_OUT_22POINT2 AUDIO_CHANNEL_OUT_MONO_HAPTIC_A AUDIO_CHANNEL_OUT_STEREO_HAPTIC_A AUDIO_CHANNEL_OUT_HAPTIC_AB AUDIO_CHANNEL_OUT_MONO_HAPTIC_AB AUDIO_CHANNEL_OUT_STEREO_HAPTIC_AB"/>
+               samplingRates="16000 22050 24000 32000 44100 48000 96000"
+               channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE"/>
     </mixPort>
 
     <mixPort name="tunneling_mix_port" role="source"
              flags="AUDIO_OUTPUT_FLAG_DIRECT AUDIO_OUTPUT_FLAG_HW_AV_SYNC" maxOpenCount="0">
       <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
-               samplingRates="16000 22050 44100 48000 96000"
-               channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE AUDIO_CHANNEL_OUT_5POINT1POINT2 AUDIO_CHANNEL_OUT_5POINT1POINT4 AUDIO_CHANNEL_OUT_6POINT1 AUDIO_CHANNEL_OUT_7POINT1 AUDIO_CHANNEL_OUT_7POINT1POINT2 AUDIO_CHANNEL_OUT_7POINT1POINT4 AUDIO_CHANNEL_OUT_13POINT_360RA AUDIO_CHANNEL_OUT_22POINT2 AUDIO_CHANNEL_OUT_MONO_HAPTIC_A AUDIO_CHANNEL_OUT_STEREO_HAPTIC_A AUDIO_CHANNEL_OUT_HAPTIC_AB AUDIO_CHANNEL_OUT_MONO_HAPTIC_AB AUDIO_CHANNEL_OUT_STEREO_HAPTIC_AB"/>
+               samplingRates="16000 22050 24000 32000 44100 48000 96000"
+               channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE"/>
       <profile name="" format="AUDIO_FORMAT_PCM_8_BIT"
-               samplingRates="16000 22050 44100 48000 96000"
-               channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE AUDIO_CHANNEL_OUT_5POINT1POINT2 AUDIO_CHANNEL_OUT_5POINT1POINT4 AUDIO_CHANNEL_OUT_6POINT1 AUDIO_CHANNEL_OUT_7POINT1 AUDIO_CHANNEL_OUT_7POINT1POINT2 AUDIO_CHANNEL_OUT_7POINT1POINT4 AUDIO_CHANNEL_OUT_13POINT_360RA AUDIO_CHANNEL_OUT_22POINT2 AUDIO_CHANNEL_OUT_MONO_HAPTIC_A AUDIO_CHANNEL_OUT_STEREO_HAPTIC_A AUDIO_CHANNEL_OUT_HAPTIC_AB AUDIO_CHANNEL_OUT_MONO_HAPTIC_AB AUDIO_CHANNEL_OUT_STEREO_HAPTIC_AB"/>
+               samplingRates="16000 22050 24000 32000 44100 48000 96000"
+               channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE"/>
       <profile name="" format="AUDIO_FORMAT_PCM_32_BIT"
-               samplingRates="16000 22050 44100 48000 96000"
-               channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE AUDIO_CHANNEL_OUT_5POINT1POINT2 AUDIO_CHANNEL_OUT_5POINT1POINT4 AUDIO_CHANNEL_OUT_6POINT1 AUDIO_CHANNEL_OUT_7POINT1 AUDIO_CHANNEL_OUT_7POINT1POINT2 AUDIO_CHANNEL_OUT_7POINT1POINT4 AUDIO_CHANNEL_OUT_13POINT_360RA AUDIO_CHANNEL_OUT_22POINT2 AUDIO_CHANNEL_OUT_MONO_HAPTIC_A AUDIO_CHANNEL_OUT_STEREO_HAPTIC_A AUDIO_CHANNEL_OUT_HAPTIC_AB AUDIO_CHANNEL_OUT_MONO_HAPTIC_AB AUDIO_CHANNEL_OUT_STEREO_HAPTIC_AB"/>
+               samplingRates="16000 22050 24000 32000 44100 48000 96000"
+               channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE"/>
       <profile name="" format="AUDIO_FORMAT_PCM_8_24_BIT"
-               samplingRates="16000 22050 44100 48000 96000"
-               channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE AUDIO_CHANNEL_OUT_5POINT1POINT2 AUDIO_CHANNEL_OUT_5POINT1POINT4 AUDIO_CHANNEL_OUT_6POINT1 AUDIO_CHANNEL_OUT_7POINT1 AUDIO_CHANNEL_OUT_7POINT1POINT2 AUDIO_CHANNEL_OUT_7POINT1POINT4 AUDIO_CHANNEL_OUT_13POINT_360RA AUDIO_CHANNEL_OUT_22POINT2 AUDIO_CHANNEL_OUT_MONO_HAPTIC_A AUDIO_CHANNEL_OUT_STEREO_HAPTIC_A AUDIO_CHANNEL_OUT_HAPTIC_AB AUDIO_CHANNEL_OUT_MONO_HAPTIC_AB AUDIO_CHANNEL_OUT_STEREO_HAPTIC_AB"/>
+               samplingRates="16000 22050 24000 32000 44100 48000 96000"
+               channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE"/>
       <profile name="" format="AUDIO_FORMAT_PCM_FLOAT"
-               samplingRates="16000 22050 44100 48000 96000"
-               channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE AUDIO_CHANNEL_OUT_5POINT1POINT2 AUDIO_CHANNEL_OUT_5POINT1POINT4 AUDIO_CHANNEL_OUT_6POINT1 AUDIO_CHANNEL_OUT_7POINT1 AUDIO_CHANNEL_OUT_7POINT1POINT2 AUDIO_CHANNEL_OUT_7POINT1POINT4 AUDIO_CHANNEL_OUT_13POINT_360RA AUDIO_CHANNEL_OUT_22POINT2 AUDIO_CHANNEL_OUT_MONO_HAPTIC_A AUDIO_CHANNEL_OUT_STEREO_HAPTIC_A AUDIO_CHANNEL_OUT_HAPTIC_AB AUDIO_CHANNEL_OUT_MONO_HAPTIC_AB AUDIO_CHANNEL_OUT_STEREO_HAPTIC_AB"/>
+               samplingRates="16000 22050 24000 32000 44100 48000 96000"
+               channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE"/>
       <profile name="" format="AUDIO_FORMAT_PCM_24_BIT_PACKED"
-               samplingRates="16000 22050 44100 48000 96000"
-               channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE AUDIO_CHANNEL_OUT_5POINT1POINT2 AUDIO_CHANNEL_OUT_5POINT1POINT4 AUDIO_CHANNEL_OUT_6POINT1 AUDIO_CHANNEL_OUT_7POINT1 AUDIO_CHANNEL_OUT_7POINT1POINT2 AUDIO_CHANNEL_OUT_7POINT1POINT4 AUDIO_CHANNEL_OUT_13POINT_360RA AUDIO_CHANNEL_OUT_22POINT2 AUDIO_CHANNEL_OUT_MONO_HAPTIC_A AUDIO_CHANNEL_OUT_STEREO_HAPTIC_A AUDIO_CHANNEL_OUT_HAPTIC_AB AUDIO_CHANNEL_OUT_MONO_HAPTIC_AB AUDIO_CHANNEL_OUT_STEREO_HAPTIC_AB"/>
+               samplingRates="16000 22050 24000 32000 44100 48000 96000"
+               channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_2POINT1 AUDIO_CHANNEL_OUT_TRI AUDIO_CHANNEL_OUT_TRI_BACK AUDIO_CHANNEL_OUT_3POINT1 AUDIO_CHANNEL_OUT_2POINT0POINT2 AUDIO_CHANNEL_OUT_2POINT1POINT2 AUDIO_CHANNEL_OUT_3POINT0POINT2 AUDIO_CHANNEL_OUT_3POINT1POINT2 AUDIO_CHANNEL_OUT_QUAD AUDIO_CHANNEL_OUT_QUAD_BACK AUDIO_CHANNEL_OUT_QUAD_SIDE AUDIO_CHANNEL_OUT_SURROUND AUDIO_CHANNEL_OUT_PENTA AUDIO_CHANNEL_OUT_5POINT1 AUDIO_CHANNEL_OUT_5POINT1_BACK AUDIO_CHANNEL_OUT_5POINT1_SIDE"/>
+    </mixPort>
+
+    <mixPort name="mmap_noirq_mix_port" role="source"
+             flags="AUDIO_OUTPUT_FLAG_DIRECT AUDIO_OUTPUT_FLAG_MMAP_NOIRQ" maxOpenCount="0">
+      <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+               samplingRates="16000 22050 24000 32000 44100 48000 96000"
+               channelMasks="AUDIO_CHANNEL_OUT_MONO AUDIO_CHANNEL_OUT_STEREO"/>
     </mixPort>
 
     <mixPort name="mixer_mix_port" role="source">
@@ -71,7 +78,7 @@
 
   <routes>
     <route type="mix" sink="MediaShell Direct Audio Device"
-           sources="direct_mix_port,tunneling_mix_port"/>
+           sources="direct_mix_port,tunneling_mix_port,mmap_noirq_mix_port"/>
 
     <route type="mix" sink="MediaShell Mixer Audio Device"
            sources="mixer_mix_port"/>
diff --git a/audio_proxy/service/device.google.atv.audio_proxy@5.1-service.rc b/audio_proxy/service/device.google.atv.audio_proxy@5.1-service.rc
index 5ce6140..3b1d713 100644
--- a/audio_proxy/service/device.google.atv.audio_proxy@5.1-service.rc
+++ b/audio_proxy/service/device.google.atv.audio_proxy@5.1-service.rc
@@ -1,6 +1,6 @@
 service audio_proxy_service /vendor/bin/hw/device.google.atv.audio_proxy@5.1-service \
   --name mediashell \
-  --stream MEDIASHELL_AUDIO_DEVICE_ADDR:40:200 \
+  --stream MEDIASHELL_AUDIO_DEVICE_ADDR:40:600 \
   --stream MEDIASHELL_MIXER_DEVICE_ADDR:40:40
     class hal
     user system
diff --git a/audio_proxy/service/device.google.atv.audio_proxy@6.0-service.rc b/audio_proxy/service/device.google.atv.audio_proxy@6.0-service.rc
index e0a9371..fa6ed77 100644
--- a/audio_proxy/service/device.google.atv.audio_proxy@6.0-service.rc
+++ b/audio_proxy/service/device.google.atv.audio_proxy@6.0-service.rc
@@ -1,6 +1,6 @@
 service audio_proxy_service /vendor/bin/hw/device.google.atv.audio_proxy@6.0-service \
   --name mediashell \
-  --stream MEDIASHELL_AUDIO_DEVICE_ADDR:40:200 \
+  --stream MEDIASHELL_AUDIO_DEVICE_ADDR:40:600 \
   --stream MEDIASHELL_MIXER_DEVICE_ADDR:40:40
     class hal
     user system
diff --git a/audio_proxy/service/device.google.atv.audio_proxy@7.0-service.rc b/audio_proxy/service/device.google.atv.audio_proxy@7.0-service.rc
index d8d2298..e8477fb 100644
--- a/audio_proxy/service/device.google.atv.audio_proxy@7.0-service.rc
+++ b/audio_proxy/service/device.google.atv.audio_proxy@7.0-service.rc
@@ -1,6 +1,6 @@
 service audio_proxy_service /vendor/bin/hw/device.google.atv.audio_proxy@7.0-service \
   --name mediashell \
-  --stream MEDIASHELL_AUDIO_DEVICE_ADDR:40:200 \
+  --stream MEDIASHELL_AUDIO_DEVICE_ADDR:40:600 \
   --stream MEDIASHELL_MIXER_DEVICE_ADDR:40:40
     class hal
     user system
diff --git a/audio_proxy/service/device.google.atv.audio_proxy@7.1-service.rc b/audio_proxy/service/device.google.atv.audio_proxy@7.1-service.rc
index 6490f71..37cab1e 100644
--- a/audio_proxy/service/device.google.atv.audio_proxy@7.1-service.rc
+++ b/audio_proxy/service/device.google.atv.audio_proxy@7.1-service.rc
@@ -1,6 +1,6 @@
 service audio_proxy_service /vendor/bin/hw/device.google.atv.audio_proxy@7.1-service \
   --name mediashell \
-  --stream MEDIASHELL_AUDIO_DEVICE_ADDR:40:200 \
+  --stream MEDIASHELL_AUDIO_DEVICE_ADDR:40:600 \
   --stream MEDIASHELL_MIXER_DEVICE_ADDR:40:40
     class hal
     user system
diff --git a/audio_proxy/service/manifest_audio_proxy_5_0.xml b/audio_proxy/service/manifest_audio_proxy_5_0.xml
index 51d3292..176b388 100644
--- a/audio_proxy/service/manifest_audio_proxy_5_0.xml
+++ b/audio_proxy/service/manifest_audio_proxy_5_0.xml
@@ -1,7 +1,7 @@
 <manifest version="1.0" type="device">
      <hal format="aidl">
         <name>device.google.atv.audio_proxy</name>
-        <version>1</version>
+        <version>3</version>
         <fqname>IAudioProxy/mediashell</fqname>
     </hal>
 
diff --git a/audio_proxy/service/manifest_audio_proxy_6_0.xml b/audio_proxy/service/manifest_audio_proxy_6_0.xml
index 0d4009a..fdb5eb1 100644
--- a/audio_proxy/service/manifest_audio_proxy_6_0.xml
+++ b/audio_proxy/service/manifest_audio_proxy_6_0.xml
@@ -1,7 +1,7 @@
 <manifest version="1.0" type="device">
      <hal format="aidl">
         <name>device.google.atv.audio_proxy</name>
-        <version>1</version>
+        <version>3</version>
         <fqname>IAudioProxy/mediashell</fqname>
     </hal>
 
diff --git a/audio_proxy/service/manifest_audio_proxy_7_0.xml b/audio_proxy/service/manifest_audio_proxy_7_0.xml
index 26f1cd9..3adb4b5 100644
--- a/audio_proxy/service/manifest_audio_proxy_7_0.xml
+++ b/audio_proxy/service/manifest_audio_proxy_7_0.xml
@@ -1,7 +1,7 @@
 <manifest version="1.0" type="device">
      <hal format="aidl">
         <name>device.google.atv.audio_proxy</name>
-        <version>1</version>
+        <version>3</version>
         <fqname>IAudioProxy/mediashell</fqname>
     </hal>
 
diff --git a/audio_proxy/service/manifest_audio_proxy_7_1.xml b/audio_proxy/service/manifest_audio_proxy_7_1.xml
index 1b3bc8b..51b93d6 100644
--- a/audio_proxy/service/manifest_audio_proxy_7_1.xml
+++ b/audio_proxy/service/manifest_audio_proxy_7_1.xml
@@ -1,7 +1,7 @@
 <manifest version="1.0" type="device">
      <hal format="aidl">
         <name>device.google.atv.audio_proxy</name>
-        <version>1</version>
+        <version>3</version>
         <fqname>IAudioProxy/mediashell</fqname>
     </hal>
 
diff --git a/libraries/BluetoothServices/Android.bp b/libraries/BluetoothServices/Android.bp
index 4631c4c..0d9ffc1 100644
--- a/libraries/BluetoothServices/Android.bp
+++ b/libraries/BluetoothServices/Android.bp
@@ -20,7 +20,6 @@
         "TwoPanelSettingsLib",
     ],
 
-    min_sdk_version: "32",
     resource_dirs: ["res"],
     manifest: "AndroidManifest.xml",
     sdk_version: "system_current",
diff --git a/libraries/BluetoothServices/lint-baseline.xml b/libraries/BluetoothServices/lint-baseline.xml
new file mode 100644
index 0000000..3b31d66
--- /dev/null
+++ b/libraries/BluetoothServices/lint-baseline.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 8.0.0-dev" type="baseline" dependencies="true" variant="all" version="8.0.0-dev">
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 33 (current min is 32): `android.bluetooth.BluetoothDevice#getBatteryLevel`"
+        errorLine1="            int battery = mDevice.getBatteryLevel();"
+        errorLine2="                                  ~~~~~~~~~~~~~~~">
+        <location
+            file="device/google/atv/libraries/BluetoothServices/src/com/google/android/tv/btservices/remote/DefaultProxy.java"
+            line="103"
+            column="35"/>
+    </issue>
+
+</issues>
\ No newline at end of file
diff --git a/libraries/BluetoothServices/src/com/google/android/tv/btservices/pairing/profiles/PairingProfileWrapperA2dp.java b/libraries/BluetoothServices/src/com/google/android/tv/btservices/pairing/profiles/PairingProfileWrapperA2dp.java
index 1284e41..9ce679f 100644
--- a/libraries/BluetoothServices/src/com/google/android/tv/btservices/pairing/profiles/PairingProfileWrapperA2dp.java
+++ b/libraries/BluetoothServices/src/com/google/android/tv/btservices/pairing/profiles/PairingProfileWrapperA2dp.java
@@ -17,7 +17,7 @@
 package com.google.android.tv.btservices.pairing.profiles;
 
 import android.bluetooth.BluetoothA2dp;
-import android.bluetooth.BluetoothDevice;;
+import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothProfile;
 
 import java.util.List;
diff --git a/libraries/BluetoothServices/src/com/google/android/tv/btservices/remote/DefaultProxy.java b/libraries/BluetoothServices/src/com/google/android/tv/btservices/remote/DefaultProxy.java
index e4c3899..d57b3dd 100644
--- a/libraries/BluetoothServices/src/com/google/android/tv/btservices/remote/DefaultProxy.java
+++ b/libraries/BluetoothServices/src/com/google/android/tv/btservices/remote/DefaultProxy.java
@@ -34,11 +34,13 @@
     private BleConnection bleConnection;
     private final boolean isBleSupported;
     private BluetoothGattCharacteristic batteryLevelCharacteristic;
+    private BluetoothGattCharacteristic versionCharacteristic;
     // return value: whether the subscription was successfully created
     private final CompletableFuture<Boolean> subscribeBatteryLevelRes;
 
     private BatteryResult lastKnownBatteryLevel = BatteryResult.RESULT_NOT_IMPLEMENTED;
     private Runnable batteryLevelCallback;
+    private Version lastKnownVersion = Version.BAD_VERSION;
 
     public DefaultProxy(Context context, BluetoothDevice device) {
         super(context, device);
@@ -129,12 +131,55 @@
 
     @Override
     public CompletableFuture<Boolean> refreshVersion(){
-        return CompletableFuture.completedFuture(true);
+        CompletableFuture<Boolean> ret = new CompletableFuture<>();
+        if (!isBleSupported || versionCharacteristic == null) {
+            ret.complete(false);
+            return ret;
+        }
+
+        bleConnection.readCharacteristic(
+                versionCharacteristic,
+                (BluetoothGattCharacteristic characteristic, int status) -> {
+                    if (status != BluetoothGatt.GATT_SUCCESS) {
+                        Log.w(TAG, "GATT failure while reading version: " + status);
+                        ret.complete(false);
+                    }
+                    String versionString = characteristic.getStringValue(0);
+                    Version version = stringToVersion(versionString);
+                    if (version == Version.BAD_VERSION) {
+                        if (DEBUG) {
+                            Log.d(TAG, "Disabling Gatt version reading for device " + mDevice
+                                    + " due to malformed version string: " + versionString);
+                        }
+                        // disable gatt version reading for this device
+                        versionCharacteristic = null;
+                    } else {
+                        if (DEBUG) {
+                            Log.d(TAG, "Received valid version via Gatt: " + version);
+                        }
+                        lastKnownVersion = version;
+                        ret.complete(true);
+                    }
+                });
+        return ret;
+    }
+
+    private Version stringToVersion(String s) {
+        if (s == null || !(s.startsWith("V") || s.startsWith("v"))) return Version.BAD_VERSION;
+        try {
+            String[] versionParts = s.split("\\.");
+            int majorVersion = Integer.parseInt(versionParts[0].substring(1));
+            int minorVersion = Integer.parseInt(versionParts[1]);
+            return new Version(majorVersion, minorVersion, 0 /*vendorId*/, 0 /*productId*/);
+        } catch (Exception e) {
+            Log.w(TAG, "Failed to parse version" + e);
+            return Version.BAD_VERSION;
+        }
     }
 
     @Override
     public Version getLastKnownVersion(){
-        return Version.BAD_VERSION;
+        return lastKnownVersion;
     }
 
     @Override
@@ -156,6 +201,20 @@
     private class Callback implements BleConnection.Callback {
         @Override
         public void onGattReady(BluetoothGatt gatt) {
+            // Get characteristic for firmware version
+            final BluetoothGattService deviceInfoService = gatt.getService(
+                    UUID_DEVICE_INFO_SERVICE);
+            if (deviceInfoService != null) {
+                versionCharacteristic =
+                        deviceInfoService.getCharacteristic(UUID_VERSION_CHARACTERISTIC);
+            }
+            if (versionCharacteristic != null) {
+                // Read firmware version
+                refreshVersion();
+            } else {
+                Log.w(TAG, "versionCharacteristic is null for device " + mDevice);
+            }
+
             // Get characteristic for battery level
             final BluetoothGattService battService = gatt.getService(UUID_BATTERY_SERVICE);
             if (battService != null) {
diff --git a/libraries/BluetoothServices/src/com/google/android/tv/btservices/remote/RemoteProxy.java b/libraries/BluetoothServices/src/com/google/android/tv/btservices/remote/RemoteProxy.java
index ec0cab6..cf5f932 100644
--- a/libraries/BluetoothServices/src/com/google/android/tv/btservices/remote/RemoteProxy.java
+++ b/libraries/BluetoothServices/src/com/google/android/tv/btservices/remote/RemoteProxy.java
@@ -37,6 +37,11 @@
             UUID.fromString("00002a19-0000-1000-8000-00805f9b34fb");
     public static final UUID CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR_UUID =
             UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
+    // Version info
+    public static final UUID UUID_DEVICE_INFO_SERVICE =
+            UUID.fromString("0000180a-0000-1000-8000-00805f9b34fb");
+    public static final UUID UUID_VERSION_CHARACTERISTIC =
+            UUID.fromString("00002A28-0000-1000-8000-00805f9b34fb");
 
     public static class Result {
 
diff --git a/libraries/BluetoothServices/src/com/google/android/tv/btservices/remote/Version.java b/libraries/BluetoothServices/src/com/google/android/tv/btservices/remote/Version.java
index 0bb491b..da94570 100644
--- a/libraries/BluetoothServices/src/com/google/android/tv/btservices/remote/Version.java
+++ b/libraries/BluetoothServices/src/com/google/android/tv/btservices/remote/Version.java
@@ -88,8 +88,8 @@
     }
 
     public String toVersionString() {
-        String major = String.valueOf(mMajorVersion);
-        String minor = String.valueOf(mMinorVersion);
+        String major = String.format("%01d", mMajorVersion);
+        String minor = String.format("%02d", mMinorVersion);
         return major + "." + minor;
     }
 
diff --git a/libraries/BluetoothServices/src/com/google/android/tv/btservices/settings/ConnectedDevicesSliceProvider.java b/libraries/BluetoothServices/src/com/google/android/tv/btservices/settings/ConnectedDevicesSliceProvider.java
index 03a66f1..56afc5d 100644
--- a/libraries/BluetoothServices/src/com/google/android/tv/btservices/settings/ConnectedDevicesSliceProvider.java
+++ b/libraries/BluetoothServices/src/com/google/android/tv/btservices/settings/ConnectedDevicesSliceProvider.java
@@ -41,6 +41,8 @@
 import android.app.PendingIntent;
 import android.app.admin.DevicePolicyManager;
 import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothClass.Device;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -93,6 +95,7 @@
             "com.google.android.settings.usage.TOGGLE_CHANGED";
     private static final String TAG = "Atv.ConDevsSliceProvider";
     private static final boolean DEBUG = false;
+    private static final boolean DISCONNECT_PREFERENCE_ENABLED = false;
     private boolean mBtDeviceServiceBound;
     private final Map<String, Version> mVersionsMap = new ConcurrentHashMap<>();
     private BluetoothDeviceService.LocalBinder mBtDeviceServiceBinder;
@@ -473,39 +476,40 @@
         }
 
         // Update "connect/disconnect preference"
-        if (showConnectDisconnectButtons(device) && cachedDevice != null
-                && !cachedDevice.isBusy()) {
+        if (cachedDevice != null && !cachedDevice.isBusy()) {
             // Whether the device is actually connected from CachedBluetoothDevice's perceptive.
             boolean isConnected = BluetoothUtils.isConnected(device) && cachedDevice.isConnected();
-            RowBuilder disconnectPref = new RowBuilder()
-                    .setKey(isConnected ? KEY_DISCONNECT : KEY_CONNECT)
-                    .setTitle(getString(isConnected
-                            ? R.string.bluetooth_disconnect : R.string.bluetooth_connect));
-            extras = new Bundle();
-            i = new Intent(context, ResponseActivity.class);
-            ResponseFragment.prepareArgs(
-                    extras,
-                    isConnected ? KEY_DISCONNECT : KEY_CONNECT,
-                    isConnected ? R.string.settings_bt_disconnect : R.string.settings_bt_connect,
-                    0,
-                    R.drawable.ic_baseline_bluetooth_searching_large,
-                    YES_NO_ARGS,
-                    deviceName,
-                    isConnected ? 1 /* default to NO (index 1) */ : 0 /* default to YES */
-            );
-            i.putExtras(extras)
-                    .putExtra(KEY_EXTRAS_DEVICE, device)
-                    .setData(Uri.parse(SCHEME_CONTENT + device.getAddress()));
-            List<String> updatedUris = Arrays.asList(GENERAL_SLICE_URI.toString(),
-                    sliceUri.toString());
-            PendingIntent updateSliceIntent = backAndUpdateSliceIntent(getContext(), 1,
-                    new ArrayList<>(updatedUris), sliceUri.toString());
-            i.putExtra(EXTRA_SLICE_FOLLOWUP, updateSliceIntent);
-            PendingIntent pendingIntent = PendingIntent
-                    .getActivity(context, 1, i,
-                            PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);
-            disconnectPref.setPendingIntent(pendingIntent);
-            psb.addPreference(disconnectPref);
+            if (!isConnected || showDisconnectButton(device, context)) {
+                RowBuilder disconnectPref = new RowBuilder()
+                        .setKey(isConnected ? KEY_DISCONNECT : KEY_CONNECT)
+                        .setTitle(getString(isConnected
+                                ? R.string.bluetooth_disconnect : R.string.bluetooth_connect));
+                extras = new Bundle();
+                i = new Intent(context, ResponseActivity.class);
+                ResponseFragment.prepareArgs(
+                        extras,
+                        isConnected ? KEY_DISCONNECT : KEY_CONNECT,
+                        isConnected ? R.string.settings_bt_disconnect : R.string.settings_bt_connect,
+                        0,
+                        R.drawable.ic_baseline_bluetooth_searching_large,
+                        YES_NO_ARGS,
+                        deviceName,
+                        isConnected ? 1 /* default to NO (index 1) */ : 0 /* default to YES */
+                );
+                i.putExtras(extras)
+                        .putExtra(KEY_EXTRAS_DEVICE, device)
+                        .setData(Uri.parse(SCHEME_CONTENT + device.getAddress()));
+                List<String> updatedUris = Arrays.asList(GENERAL_SLICE_URI.toString(),
+                        sliceUri.toString());
+                PendingIntent updateSliceIntent = backAndUpdateSliceIntent(getContext(), 1,
+                        new ArrayList<>(updatedUris), sliceUri.toString());
+                i.putExtra(EXTRA_SLICE_FOLLOWUP, updateSliceIntent);
+                PendingIntent pendingIntent = PendingIntent
+                        .getActivity(context, 1, i,
+                                PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);
+                disconnectPref.setPendingIntent(pendingIntent);
+                psb.addPreference(disconnectPref);
+            }
         }
 
         // Update "rename preference".
@@ -709,8 +713,13 @@
         return Configuration.get(getContext()).isEnabled(R.bool.show_cec_in_connected_settings);
     }
 
-    private boolean showConnectDisconnectButtons(BluetoothDevice device) {
-        return !BluetoothUtils.isBluetoothDeviceMetadataInList(
+    private boolean showDisconnectButton(BluetoothDevice device, Context context) {
+        if (DISCONNECT_PREFERENCE_ENABLED) {
+            return true;
+        }
+        return !BluetoothUtils.isRemoteClass(device)
+            && !BluetoothUtils.isRemote(context, device)
+            && !BluetoothUtils.isBluetoothDeviceMetadataInList(
                 getContext(),
                 device,
                 BluetoothDevice.METADATA_MODEL_NAME,
diff --git a/overlay/TvDisableLowPowerStandbyOverlay/Android.bp b/overlay/TvDisableLowPowerStandbyOverlay/Android.bp
new file mode 100644
index 0000000..c08c2d3
--- /dev/null
+++ b/overlay/TvDisableLowPowerStandbyOverlay/Android.bp
@@ -0,0 +1,16 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "device_google_atv_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["device_google_atv_license"],
+}
+
+runtime_resource_overlay {
+    name: "TvDisableLowPowerStandbyOverlay",
+    certificate: "platform",
+    resource_dirs: ["res"],
+    product_specific: true,
+    sdk_version: "current",
+}
diff --git a/overlay/TvDisableLowPowerStandbyOverlay/AndroidManifest.xml b/overlay/TvDisableLowPowerStandbyOverlay/AndroidManifest.xml
new file mode 100644
index 0000000..33700c3
--- /dev/null
+++ b/overlay/TvDisableLowPowerStandbyOverlay/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<!--
+  Copyright (C) 2023 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.tv.overlay.lps.disabled"
+    android:versionCode="1"
+    android:versionName="1.0" >
+
+    <application android:hasCode="false" />
+
+    <overlay
+        android:targetPackage="android"
+        android:priority="1"
+        android:isStatic="true" />
+
+</manifest>
diff --git a/overlay/TvDisableLowPowerStandbyOverlay/res/values/config.xml b/overlay/TvDisableLowPowerStandbyOverlay/res/values/config.xml
new file mode 100644
index 0000000..0f30d7c
--- /dev/null
+++ b/overlay/TvDisableLowPowerStandbyOverlay/res/values/config.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 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.
+-->
+
+<resources>
+    <!-- Whether Low Power Standby is supported and can be enabled. -->
+    <bool name="config_lowPowerStandbySupported">false</bool>
+</resources>
+
diff --git a/overlay/TvFrameworkOverlay/res/values-de/strings.xml b/overlay/TvFrameworkOverlay/res/values-de/strings.xml
index 7ab517e..8931686 100644
--- a/overlay/TvFrameworkOverlay/res/values-de/strings.xml
+++ b/overlay/TvFrameworkOverlay/res/values-de/strings.xml
@@ -18,8 +18,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="accessibility_shortcut_multiple_service_warning" msgid="7133244216041378936">"Du hast die Zurück-Taste und die Taste „Leiser“ drei Sekunden lang gedrückt gehalten, um <xliff:g id="SERVICE_0">%1$s</xliff:g> zu verwenden.\n\n Wenn du <xliff:g id="SERVICE_1">%1$s</xliff:g> jetzt aktivieren möchtest, halte die Zurück-Taste und die Taste „Leiser“ noch einmal drei Sekunden lang gedrückt. Du kannst diese Tastenkombination jederzeit verwenden, um <xliff:g id="SERVICE_2">%1$s</xliff:g> zu aktivieren oder zu deaktivieren.\n\n Unter Einstellungen &gt; System &gt; Bedienungshilfen kannst du die Einstellungen anpassen."</string>
     <string name="accessibility_shortcut_toogle_warning" msgid="6107141001991769734">"Wenn die Tastenkombination aktiviert ist, kannst du die Zurück-Taste und die Taste „Leiser“ drei Sekunden lang gedrückt halten, um eine Bedienungshilfe zu starten.\n\n Aktuelle Bedienungshilfe:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Du kannst die Bedienungshilfe unter „Einstellungen“ &gt; „Bedienungshilfen“ ändern."</string>
-    <string name="accessibility_shortcut_enabling_service" msgid="955379455142747901">"Du hast die Zurück-Taste und die Taste „Leiser“ gedrückt. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ist aktiviert."</string>
-    <string name="accessibility_shortcut_disabling_service" msgid="1407311966343470931">"Du hast die Zurück-Taste und die Taste „Leiser“ gedrückt. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ist deaktiviert."</string>
+    <string name="accessibility_shortcut_enabling_service" msgid="955379455142747901">"Zurück- und Nach-Unten-Taste gleichzeitig gedrückt. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> wurde aktiviert."</string>
+    <string name="accessibility_shortcut_disabling_service" msgid="1407311966343470931">"Zurück- und Nach-Unten-Taste gleichzeitig gedrückt. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> wurde deaktiviert."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="7263788823743141556">"Du hast die Zurück-Taste und die Taste „Leiser“ drei Sekunden lang gedrückt gehalten, um <xliff:g id="SERVICE_0">%1$s</xliff:g> zu verwenden. Wenn du <xliff:g id="SERVICE_1">%1$s</xliff:g> jetzt aktivieren möchtest, halte die Zurück-Taste und die Taste „Leiser“ noch einmal drei Sekunden lang gedrückt. Du kannst diese Tastenkombination jederzeit verwenden, um <xliff:g id="SERVICE_2">%1$s</xliff:g> zu aktivieren oder zu deaktivieren."</string>
     <string name="accessibility_shortcut_single_service_warning" msgid="7941823324711523679">"Du hast die Zurück-Taste und die Taste „Leiser“ drei Sekunden lang gedrückt gehalten, um <xliff:g id="SERVICE_0">%1$s</xliff:g> zu verwenden.\n\n Wenn du <xliff:g id="SERVICE_1">%1$s</xliff:g> jetzt aktivieren möchtest, halte die Zurück-Taste und die Taste „Leiser“ noch einmal drei Sekunden lang gedrückt.\n Du kannst diese Tastenkombination jederzeit verwenden, um <xliff:g id="SERVICE_2">%1$s</xliff:g> zu aktivieren oder zu deaktivieren.\n\n Unter Einstellungen &gt; System &gt; Bedienungshilfen kannst du die Einstellungen anpassen."</string>
     <string name="disable_accessibility_shortcut" msgid="4559312586447750126">"Später"</string>
diff --git a/overlay/TvFrameworkOverlay/res/values-en-rCA/strings.xml b/overlay/TvFrameworkOverlay/res/values-en-rCA/strings.xml
index 9f7cbab..6ab3006 100644
--- a/overlay/TvFrameworkOverlay/res/values-en-rCA/strings.xml
+++ b/overlay/TvFrameworkOverlay/res/values-en-rCA/strings.xml
@@ -16,12 +16,12 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="accessibility_shortcut_multiple_service_warning" msgid="7133244216041378936">"You have held both the back and down buttons for three seconds to use <xliff:g id="SERVICE_0">%1$s</xliff:g>.\n\n To enable <xliff:g id="SERVICE_1">%1$s</xliff:g> now, hold the back and down buttons for three seconds again. Use this shortcut at any time to enable or disable <xliff:g id="SERVICE_2">%1$s</xliff:g>.\n\n You can adjust your preferences in Settings &gt; System &gt; Accessibility."</string>
-    <string name="accessibility_shortcut_toogle_warning" msgid="6107141001991769734">"When the shortcut is on, pressing both the back and down buttons for three seconds will start an accessibility feature.\n\n Current accessibility feature:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n You can change the feature in Settings &gt; Accessibility."</string>
+    <string name="accessibility_shortcut_multiple_service_warning" msgid="7133244216041378936">"You have held both the back and down buttons for three seconds to use <xliff:g id="SERVICE_0">%1$s</xliff:g>.\n\n To enable <xliff:g id="SERVICE_1">%1$s</xliff:g> now, hold the back and down buttons for three seconds again. Use this shortcut anytime to enable or disable <xliff:g id="SERVICE_2">%1$s</xliff:g>.\n\n You can adjust your preferences in Settings &gt; System &gt; Accessibility."</string>
+    <string name="accessibility_shortcut_toogle_warning" msgid="6107141001991769734">"When the shortcut is on, pressing both the back and down buttons for 3 seconds will start an accessibility feature.\n\n Current accessibility feature:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n You can change the feature in Settings &gt; Accessibility."</string>
     <string name="accessibility_shortcut_enabling_service" msgid="955379455142747901">"Held back and down buttons. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned on."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="1407311966343470931">"Held back and down buttons. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned off."</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="7263788823743141556">"You have held both the back and down buttons for three seconds to use <xliff:g id="SERVICE_0">%1$s</xliff:g>. To enable <xliff:g id="SERVICE_1">%1$s</xliff:g> now, hold the back and down buttons for three seconds again. Use this shortcut at any time to enable or disable <xliff:g id="SERVICE_2">%1$s</xliff:g>."</string>
-    <string name="accessibility_shortcut_single_service_warning" msgid="7941823324711523679">"You have held both the back and down buttons for three seconds to use <xliff:g id="SERVICE_0">%1$s</xliff:g>.\n\n To enable <xliff:g id="SERVICE_1">%1$s</xliff:g> now, hold the back and down buttons for three seconds again.\n Use this shortcut at any time to enable or disable <xliff:g id="SERVICE_2">%1$s</xliff:g>.\n\n You can adjust your preferences in Settings &gt; System &gt; Accessibility."</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="7263788823743141556">"You have held both the back and down buttons for three seconds to use <xliff:g id="SERVICE_0">%1$s</xliff:g>. To enable <xliff:g id="SERVICE_1">%1$s</xliff:g> now, hold the back and down buttons for three seconds again. Use this shortcut anytime to enable or disable <xliff:g id="SERVICE_2">%1$s</xliff:g>."</string>
+    <string name="accessibility_shortcut_single_service_warning" msgid="7941823324711523679">"You have held both the back and down buttons for three seconds to use <xliff:g id="SERVICE_0">%1$s</xliff:g>.\n\n To enable <xliff:g id="SERVICE_1">%1$s</xliff:g> now, hold the back and down buttons for three seconds again.\n Use this shortcut anytime to enable or disable <xliff:g id="SERVICE_2">%1$s</xliff:g>.\n\n You can adjust your preferences in Settings &gt; System &gt; Accessibility."</string>
     <string name="disable_accessibility_shortcut" msgid="4559312586447750126">"Not now"</string>
     <string name="leave_accessibility_shortcut_on" msgid="6807632291651241490">"Turn on now"</string>
 </resources>
diff --git a/overlay/TvFrameworkOverlay/res/values-fa/strings.xml b/overlay/TvFrameworkOverlay/res/values-fa/strings.xml
index da10234..949f7cb 100644
--- a/overlay/TvFrameworkOverlay/res/values-fa/strings.xml
+++ b/overlay/TvFrameworkOverlay/res/values-fa/strings.xml
@@ -22,6 +22,6 @@
     <string name="accessibility_shortcut_disabling_service" msgid="1407311966343470931">"دکمه‌های برگشت و پایین را نگه داشتید. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> خاموش شد."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="7263788823743141556">"برای استفاده از <xliff:g id="SERVICE_0">%1$s</xliff:g>، دکمه‌های برگشت و پایین را با هم به‌مدت سه ثانیه نگه داشته‌اید. برای اینکه <xliff:g id="SERVICE_1">%1$s</xliff:g> اکنون فعال شود، دکمه‌های برگشت و پایین را دوباره به‌مدت سه ثانیه نگه دارید. هر زمان خواستید برای فعال یا غیرفعال کردن <xliff:g id="SERVICE_2">%1$s</xliff:g>، از این میان‌بر استفاده کنید."</string>
     <string name="accessibility_shortcut_single_service_warning" msgid="7941823324711523679">"برای استفاده از <xliff:g id="SERVICE_0">%1$s</xliff:g>، دکمه‌های برگشت و پایین را با هم به‌مدت سه ثانیه نگه داشته‌اید.\n\n برای اینکه <xliff:g id="SERVICE_1">%1$s</xliff:g> اکنون فعال شود، دکمه‌های برگشت و پایین را دوباره به‌مدت سه ثانیه نگه دارید.\n هر زمان خواستید برای فعال یا غیرفعال کردن <xliff:g id="SERVICE_2">%1$s</xliff:g>، از این میان‌بر استفاده کنید.\n\n می‌توانید اولویت‌ها را در «تنظیمات &gt; سیستم &gt; دسترس‌پذیری» تنظیم کنید."</string>
-    <string name="disable_accessibility_shortcut" msgid="4559312586447750126">"اکنون نه"</string>
+    <string name="disable_accessibility_shortcut" msgid="4559312586447750126">"حالا نه"</string>
     <string name="leave_accessibility_shortcut_on" msgid="6807632291651241490">"اکنون روشن شود"</string>
 </resources>
diff --git a/overlay/TvFrameworkOverlay/res/values-ky/strings.xml b/overlay/TvFrameworkOverlay/res/values-ky/strings.xml
index 1b5d5ec..278b60d 100644
--- a/overlay/TvFrameworkOverlay/res/values-ky/strings.xml
+++ b/overlay/TvFrameworkOverlay/res/values-ky/strings.xml
@@ -16,12 +16,12 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="accessibility_shortcut_multiple_service_warning" msgid="7133244216041378936">"<xliff:g id="SERVICE_0">%1$s</xliff:g> кызматын колдонуу үчүн артка жана ылдый баскычтарын үч секунддай коё бербей басып турдуңуз.\n\n <xliff:g id="SERVICE_1">%1$s</xliff:g> кызматын азыр иштетүү үчүн артка жана ылдый баскычтарын үч секунддай коё бербей басып туруңуз. <xliff:g id="SERVICE_2">%1$s</xliff:g> кызматын каалаган убакта ушул ыкчам баскыч менен иштетип же өчүрө аласыз.\n\n Параметрлерди тууралоо үчүн Жөндөөлөр &gt; Тутум &gt; Атайын мүмкүнчүлүктөргө өтүңүз."</string>
-    <string name="accessibility_shortcut_toogle_warning" msgid="6107141001991769734">"Атайын мүмкүнчүлүктөр функциясын пайдалануу үчүн ал күйгүзүлгөндө, үндү катуулатып/акырындаткан эки баскычты тең 3 секунддай коё бербей басып туруңуз.\n\n Учурдагы атайын мүмкүнчүлүктөрдүн жөндөөлөрү:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Жөндөөлөр &gt; Атайын мүмкүнчүлүктөр бөлүмүнөн өзгөртө аласыз."</string>
+    <string name="accessibility_shortcut_multiple_service_warning" msgid="7133244216041378936">"<xliff:g id="SERVICE_0">%1$s</xliff:g> кызматын колдонуу үчүн артка жана ылдый баскычтарын үч секунддай коё бербей басып турдуңуз.\n\n <xliff:g id="SERVICE_1">%1$s</xliff:g> кызматын азыр иштетүү үчүн артка жана ылдый баскычтарын үч секунддай коё бербей басып туруңуз. <xliff:g id="SERVICE_2">%1$s</xliff:g> кызматын каалаган убакта ушул ыкчам баскыч менен иштетип же өчүрө аласыз.\n\n Параметрлерди тууралоо үчүн Параметрлер &gt; Тутум &gt; Атайын мүмкүнчүлүктөргө өтүңүз."</string>
+    <string name="accessibility_shortcut_toogle_warning" msgid="6107141001991769734">"Атайын мүмкүнчүлүктөр функциясын пайдалануу үчүн ал күйгүзүлгөндө, үндү катуулатып/акырындаткан эки баскычты тең 3 секунддай коё бербей басып туруңуз.\n\n Учурдагы атайын мүмкүнчүлүктөрдүн параметрлери:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Параметрлер &gt; Атайын мүмкүнчүлүктөр бөлүмүнөн өзгөртө аласыз."</string>
     <string name="accessibility_shortcut_enabling_service" msgid="955379455142747901">"Артка жана ылдый баскычтарын коё бербей басып турдуңуз. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> күйгүзүлдү."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="1407311966343470931">"Артка жана ылдый баскычтарын коё бербей басып турдуңуз. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> өчүрүлдү."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="7263788823743141556">"<xliff:g id="SERVICE_0">%1$s</xliff:g> кызматын колдонуу үчүн артка жана ылдый баскычтарын үч секунддай коё бербей басып турдуңуз. <xliff:g id="SERVICE_1">%1$s</xliff:g> кызматын азыр иштетүү үчүн артка жана ылдый баскычтарын үч секунддай коё бербей басып туруңуз. <xliff:g id="SERVICE_2">%1$s</xliff:g> кызматын каалаган убакта ушул ыкчам баскыч менен иштетип же өчүрө аласыз."</string>
-    <string name="accessibility_shortcut_single_service_warning" msgid="7941823324711523679">"<xliff:g id="SERVICE_0">%1$s</xliff:g> кызматын колдонуу үчүн артка жана ылдый баскычтарын үч секунддай коё бербей басып турдуңуз.\n\n <xliff:g id="SERVICE_1">%1$s</xliff:g> кызматын азыр иштетүү үчүн артка жана ылдый баскычтарын үч секунддай коё бербей басып туруңуз.\n <xliff:g id="SERVICE_2">%1$s</xliff:g> кызматын каалаган убакта ушул ыкчам баскыч менен иштетип же өчүрө аласыз.\n\n Параметрлерди тууралоо үчүн Жөндөөлөр &gt; Тутум &gt; Атайын мүмкүнчүлүктөргө өтүңүз."</string>
+    <string name="accessibility_shortcut_single_service_warning" msgid="7941823324711523679">"<xliff:g id="SERVICE_0">%1$s</xliff:g> кызматын колдонуу үчүн артка жана ылдый баскычтарын үч секунддай коё бербей басып турдуңуз.\n\n <xliff:g id="SERVICE_1">%1$s</xliff:g> кызматын азыр иштетүү үчүн артка жана ылдый баскычтарын үч секунддай коё бербей басып туруңуз.\n <xliff:g id="SERVICE_2">%1$s</xliff:g> кызматын каалаган убакта ушул ыкчам баскыч менен иштетип же өчүрө аласыз.\n\n Параметрлерди тууралоо үчүн Параметрлер &gt; Тутум &gt; Атайын мүмкүнчүлүктөргө өтүңүз."</string>
     <string name="disable_accessibility_shortcut" msgid="4559312586447750126">"Азыр эмес"</string>
     <string name="leave_accessibility_shortcut_on" msgid="6807632291651241490">"Күйгүзүү"</string>
 </resources>
diff --git a/overlay/TvFrameworkOverlay/res/values-or/strings.xml b/overlay/TvFrameworkOverlay/res/values-or/strings.xml
index fa73c76..06045f2 100644
--- a/overlay/TvFrameworkOverlay/res/values-or/strings.xml
+++ b/overlay/TvFrameworkOverlay/res/values-or/strings.xml
@@ -16,8 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="accessibility_shortcut_multiple_service_warning" msgid="7133244216041378936">"<xliff:g id="SERVICE_0">%1$s</xliff:g> ବ୍ୟବହାର କରିବାକୁ ଆପଣ ତିନି ସେକେଣ୍ଡ ପାଇଁ ଉଭୟ ବ୍ୟାକ୍ ଓ ଡାଉନ୍ ବଟନକୁ ଧରି ରଖିଛନ୍ତି।\n\n ବର୍ତ୍ତମାନ <xliff:g id="SERVICE_1">%1$s</xliff:g>କୁ ସକ୍ଷମ କରିବା ପାଇଁ, ପୁଣି ତିନି ସେକେଣ୍ଡ ପାଇଁ ବ୍ୟାକ୍ ଓ ଡାଉନ୍ ବଟନକୁ ଧରି ରଖନ୍ତୁ। <xliff:g id="SERVICE_2">%1$s</xliff:g>କୁ ସକ୍ଷମ କିମ୍ବା ଅକ୍ଷମ କରିବା ପାଇଁ ଯେ କୌଣସି ସମୟରେ ଏହି ସର୍ଟକଟ୍ ବ୍ୟବହାର କରନ୍ତୁ।\n\n ଆପଣ ସେଟିଂସ୍ &gt; ସିଷ୍ଟମ୍ &gt; ଆକ୍ସେସିବିଲିଟୀରେ ଆପଣଙ୍କ ପସନ୍ଦଗୁଡ଼ିକୁ ଆଡଜଷ୍ଟ କରିପାରିବେ।"</string>
-    <string name="accessibility_shortcut_toogle_warning" msgid="6107141001991769734">"ସର୍ଟକଟ୍ ଚାଲୁ ଥିବା ବେଳେ, ଉଭୟ ବ୍ୟାକ୍ ଓ ଡାଉନ୍ ବଟନକୁ 3 ସେକେଣ୍ଡ ପାଇଁ ଦବାଇବା ଫଳରେ ଏକ ଆକ୍ସେସିବିଲିଟୀ ଫିଚର୍ ଆରମ୍ଭ ହେବ।\n\n ବର୍ତ୍ତମାନର ଆକ୍ସେସିବିଲିଟୀ ଫିଚର୍:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n ଆପଣ ସେଟିଂସ୍ &gt; ଆକ୍ସେସିବିଲିଟୀରେ ଫିଚରକୁ ପରିବର୍ତ୍ତନ କରିପାରିବେ।"</string>
+    <string name="accessibility_shortcut_multiple_service_warning" msgid="7133244216041378936">"<xliff:g id="SERVICE_0">%1$s</xliff:g> ବ୍ୟବହାର କରିବାକୁ ଆପଣ ତିନି ସେକେଣ୍ଡ ପାଇଁ ଉଭୟ ବ୍ୟାକ ଓ ଡାଉନ ବଟନକୁ ଧରି ରଖିଛନ୍ତି।\n\n ବର୍ତ୍ତମାନ <xliff:g id="SERVICE_1">%1$s</xliff:g>କୁ ସକ୍ଷମ କରିବା ପାଇଁ, ପୁଣି ତିନି ସେକେଣ୍ଡ ପାଇଁ ବ୍ୟାକ ଓ ଡାଉନ ବଟନକୁ ଧରି ରଖନ୍ତୁ। <xliff:g id="SERVICE_2">%1$s</xliff:g>କୁ ସକ୍ଷମ କିମ୍ବା ଅକ୍ଷମ କରିବା ପାଇଁ ଯେ କୌଣସି ସମୟରେ ଏହି ସର୍ଟକଟ ବ୍ୟବହାର କରନ୍ତୁ।\n\n ଆପଣ ସେଟିଂସ &gt; ସିଷ୍ଟମ &gt; ଆକ୍ସେସିବିଲିଟୀରେ ଆପଣଙ୍କ ପସନ୍ଦଗୁଡ଼ିକୁ ଆଡଜଷ୍ଟ କରିପାରିବେ।"</string>
+    <string name="accessibility_shortcut_toogle_warning" msgid="6107141001991769734">"ସର୍ଟକଟ ଚାଲୁ ଥିବା ବେଳେ, ଉଭୟ ବ୍ୟାକ ଓ ଡାଉନ ବଟନକୁ 3 ସେକେଣ୍ଡ ପାଇଁ ଦବାଇବା ଫଳରେ ଏକ ଆକ୍ସେସିବିଲିଟୀ ଫିଚର ଆରମ୍ଭ ହେବ।\n\n ବର୍ତ୍ତମାନର ଆକ୍ସେସିବିଲିଟୀ ଫିଚର:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n ଆପଣ ସେଟିଂସ &gt; ଆକ୍ସେସିବିଲିଟୀରେ ଫିଚରକୁ ପରିବର୍ତ୍ତନ କରିପାରିବେ।"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="955379455142747901">"ବ୍ୟାକ୍ ଓ ଡାଉନ୍ ବଟନକୁ ଧରି ରଖନ୍ତୁ। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ଚାଲୁ କରାଯାଇଛି।"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="1407311966343470931">"ବ୍ୟାକ୍ ଓ ଡାଉନ୍ ବଟନକୁ ଧରି ରଖନ୍ତୁ। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ବନ୍ଦ କରାଯାଇଛି।"</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="7263788823743141556">"<xliff:g id="SERVICE_0">%1$s</xliff:g> ବ୍ୟବହାର କରିବାକୁ ଆପଣ ତିନି ସେକେଣ୍ଡ ପାଇଁ ଉଭୟ ବ୍ୟାକ୍ ଓ ଡାଉନ୍ ବଟନକୁ ଧରି ରଖିଛନ୍ତି। ବର୍ତ୍ତମାନ <xliff:g id="SERVICE_1">%1$s</xliff:g>କୁ ସକ୍ଷମ କରିବା ପାଇଁ, ପୁଣି ତିନି ସେକେଣ୍ଡ ପାଇଁ ବ୍ୟାକ୍ ଓ ଡାଉନ୍ ବଟନକୁ ଧରି ରଖନ୍ତୁ। <xliff:g id="SERVICE_2">%1$s</xliff:g>କୁ ସକ୍ଷମ କିମ୍ବା ଅକ୍ଷମ କରିବା ପାଇଁ ଯେ କୌଣସି ସମୟରେ ଏହି ସର୍ଟକଟ୍ ବ୍ୟବହାର କରନ୍ତୁ।"</string>
diff --git a/overlay/TvFrameworkOverlay/res/values-ro/strings.xml b/overlay/TvFrameworkOverlay/res/values-ro/strings.xml
index cdc79c6..2df2bfa 100644
--- a/overlay/TvFrameworkOverlay/res/values-ro/strings.xml
+++ b/overlay/TvFrameworkOverlay/res/values-ro/strings.xml
@@ -16,12 +16,12 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="accessibility_shortcut_multiple_service_warning" msgid="7133244216041378936">"Ați apăsat lung butoanele înapoi și în jos timp de trei secunde pentru a folosi <xliff:g id="SERVICE_0">%1$s</xliff:g>.\n\n Pentru a activa <xliff:g id="SERVICE_1">%1$s</xliff:g> acum, apăsați din nou butoanele înapoi și în jos timp de trei secunde. Folosiți oricând această comandă rapidă pentru a activa sau a dezactiva <xliff:g id="SERVICE_2">%1$s</xliff:g>.\n\n Vă puteți ajusta preferințele în Setări &gt; Sistem &gt; Accesibilitate."</string>
-    <string name="accessibility_shortcut_toogle_warning" msgid="6107141001991769734">"Când comanda rapidă este activată, dacă apăsați simultan butoanele înapoi și în jos timp de trei secunde, veți lansa o funcție de accesibilitate.\n\n Funcția de accesibilitate actuală:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Puteți modifica funcția în Setări și accesibilitate."</string>
-    <string name="accessibility_shortcut_enabling_service" msgid="955379455142747901">"Ați apăsat lung butoanele înapoi și în jos. S-a activat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
-    <string name="accessibility_shortcut_disabling_service" msgid="1407311966343470931">"Ați apăsat lung butoanele înapoi și în jos. S-a dezactivat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
-    <string name="accessibility_shortcut_spoken_feedback" msgid="7263788823743141556">"Ați apăsat lung butoanele înapoi și în jos timp de trei secunde pentru a folosi <xliff:g id="SERVICE_0">%1$s</xliff:g>. Pentru a activa <xliff:g id="SERVICE_1">%1$s</xliff:g> acum, apăsați din nou butoanele înapoi și în jos timp de trei secunde. Folosiți oricând această comandă rapidă pentru a activa sau a dezactiva <xliff:g id="SERVICE_2">%1$s</xliff:g>."</string>
-    <string name="accessibility_shortcut_single_service_warning" msgid="7941823324711523679">"Ați apăsat lung butoanele înapoi și în jos timp de trei secunde pentru a folosi <xliff:g id="SERVICE_0">%1$s</xliff:g>.\n\n Pentru a activa <xliff:g id="SERVICE_1">%1$s</xliff:g> acum, apăsați din nou butoanele înapoi și în jos timp de trei secunde.\n Folosiți oricând această comandă rapidă pentru a activa sau a dezactiva <xliff:g id="SERVICE_2">%1$s</xliff:g>.\n\n Vă puteți ajusta preferințele în Setări &gt; Sistem &gt; Accesibilitate."</string>
+    <string name="accessibility_shortcut_multiple_service_warning" msgid="7133244216041378936">"Ai apăsat lung butoanele înapoi și în jos timp de trei secunde pentru a folosi <xliff:g id="SERVICE_0">%1$s</xliff:g>.\n\n Pentru a activa <xliff:g id="SERVICE_1">%1$s</xliff:g> acum, apasă din nou butoanele înapoi și în jos timp de trei secunde. Folosește oricând această comandă rapidă pentru a activa sau a dezactiva <xliff:g id="SERVICE_2">%1$s</xliff:g>.\n\n Îți poți ajusta preferințele în Setări &gt; Sistem &gt; Accesibilitate."</string>
+    <string name="accessibility_shortcut_toogle_warning" msgid="6107141001991769734">"Când comanda rapidă este activată, dacă apeși simultan butoanele înapoi și în jos timp de trei secunde, vei lansa o funcție de accesibilitate.\n\n Funcția de accesibilitate actuală:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Poți modifica funcția în Setări și accesibilitate."</string>
+    <string name="accessibility_shortcut_enabling_service" msgid="955379455142747901">"Ai apăsat lung butoanele înapoi și în jos. S-a activat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
+    <string name="accessibility_shortcut_disabling_service" msgid="1407311966343470931">"Ai apăsat lung butoanele înapoi și în jos. S-a dezactivat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
+    <string name="accessibility_shortcut_spoken_feedback" msgid="7263788823743141556">"Ai apăsat lung butoanele înapoi și în jos timp de trei secunde pentru a folosi <xliff:g id="SERVICE_0">%1$s</xliff:g>. Pentru a activa <xliff:g id="SERVICE_1">%1$s</xliff:g> acum, apasă din nou butoanele înapoi și în jos timp de trei secunde. Folosește oricând această comandă rapidă pentru a activa sau a dezactiva <xliff:g id="SERVICE_2">%1$s</xliff:g>."</string>
+    <string name="accessibility_shortcut_single_service_warning" msgid="7941823324711523679">"Ai apăsat lung butoanele înapoi și în jos timp de trei secunde pentru a folosi <xliff:g id="SERVICE_0">%1$s</xliff:g>.\n\n Pentru a activa <xliff:g id="SERVICE_1">%1$s</xliff:g> acum, apasă din nou butoanele înapoi și în jos timp de trei secunde.\n Folosește oricând această comandă rapidă pentru a activa sau a dezactiva <xliff:g id="SERVICE_2">%1$s</xliff:g>.\n\n Îți poți ajusta preferințele în Setări &gt; Sistem &gt; Accesibilitate."</string>
     <string name="disable_accessibility_shortcut" msgid="4559312586447750126">"Nu acum"</string>
-    <string name="leave_accessibility_shortcut_on" msgid="6807632291651241490">"Activați acum"</string>
+    <string name="leave_accessibility_shortcut_on" msgid="6807632291651241490">"Activează acum"</string>
 </resources>
diff --git a/overlay/TvFrameworkOverlay/res/values/config.xml b/overlay/TvFrameworkOverlay/res/values/config.xml
index eba2806..1cbfc11 100644
--- a/overlay/TvFrameworkOverlay/res/values/config.xml
+++ b/overlay/TvFrameworkOverlay/res/values/config.xml
@@ -20,23 +20,31 @@
          is disableable for products for which it is irrelevant. -->
     <bool name="config_useVolumeKeySounds">false</bool>
 
-    <!-- Disable AUDIO_BECOMING_NOISY notifications. -->
+    <!-- Flag indicating whether the AUDIO_BECOMING_NOISY notification should
+         be sent during a change to the audio output device. -->
     <bool name="config_sendAudioBecomingNoisy">false</bool>
 
-    <!-- This device is data-only. -->
-    <bool name="config_voice_capable">false</bool>
+    <!-- Set this to true to enable the platform's auto-power-save modes like doze and
+         app standby.  These are not enabled by default because they require a standard
+         cloud-to-device messaging service for apps to interact correctly with the modes
+         (such as to be able to deliver an instant message to the device even when it is
+         dozing).  This should be enabled if you have such services and expect apps to
+         correctly use them when installed on your device.  Otherwise, keep this disabled
+         so that applications can still use their own mechanisms. -->
+    <bool name="config_enableAutoPowerModes">true</bool>
 
-    <!-- This device does not allow sms service. -->
-    <bool name="config_sms_capable">false</bool>
-
-    <!-- This device does not support mobile data. -->
-    <bool name="config_mobile_data_capable">false</bool>
+    <!-- Whether (if true) this is a kind of device that can be moved around (eg. phone/laptop),
+         or (if false) something for which movement is either not measurable or should not count
+         toward power states (eg. tv/soundbar). -->
+    <bool name="config_autoPowerModeUseMotionSensor">false</bool>
 
     <!-- Control the default UI mode type to use when there is no other type override
          happening.  One of the following values (See Configuration.java):
              1  UI_MODE_TYPE_NORMAL
              4  UI_MODE_TYPE_TELEVISION
              5  UI_MODE_TYPE_APPLIANCE
+             6  UI_MODE_TYPE_WATCH
+             7  UI_MODE_TYPE_VR_HEADSET
          Any other values will have surprising consequences. -->
     <integer name="config_defaultUiModeType">4</integer>
 
@@ -52,8 +60,20 @@
     -->
     <integer name="config_defaultNightMode">2</integer>
 
-    <!-- default device has recents property -->
-    <bool name="config_hasRecents">false</bool>
+    <!-- Control the behavior when the user long presses the power button.
+            0 - Nothing
+            1 - Global actions menu
+            2 - Power off (with confirmation)
+            3 - Power off (without confirmation)
+            4 - Go to voice assist
+            5 - Go to assistant (Settings.Secure.ASSISTANT)
+    -->
+    <integer name="config_longPressOnPowerBehavior">3</integer>
+
+    <!-- If true, enables verification of the lockscreen credential in the factory reset protection
+        flow. This should be true if gatekeeper / weaver credentials can still be checked after a
+        factory reset. -->
+    <bool name="config_enableCredentialFactoryResetProtection">false</bool>
 
     <!-- Control the behavior when the user long presses the home button.
             0 - Nothing
@@ -74,76 +94,117 @@
     -->
     <integer name="config_doubleTapOnHomeBehavior">2</integer>
 
-    <!-- Override configuration check for dpad so that we always appear to have one -->
+    <!-- Whether the geolocation time zone detection feature is enabled. Setting this to false means
+         the feature cannot be used. Setting this to true means system server components can be
+         tested and location time zone detection may be used if other configuration allows (see
+         location time zone provider configuration settings below). -->
+    <bool name="config_enableGeolocationTimeZoneDetection" translatable="false">false</bool>
+
+    <!-- Flag indicating whether the current device is "voice capable".
+         If true, this means that the device supports circuit-switched
+         (i.e. voice) phone calls over the telephony network, and is
+         allowed to display the in-call UI while a cellular voice call is
+         active.  This can be overridden to false for "data only" devices
+         which can't make voice calls and don't support any in-call UI.
+
+         Note: this flag is subtly different from the
+         PackageManager.FEATURE_TELEPHONY system feature, which is
+         available on *any* device with a telephony radio, even if the
+         device is data-only. -->
+    <bool name="config_voice_capable">false</bool>
+
+    <!-- Flag indicating whether the current device allows sms service.
+         If true, this means that the device supports both sending and
+         receiving sms via the telephony network.
+         This can be overridden to false for "data only" devices
+         which can't send and receive sms message.
+
+         Note: Disable SMS also disable voicemail waiting sms,
+               cell broadcasting sms, and MMS. -->
+    <bool name="config_sms_capable">false</bool>
+
+    <!-- Flag indicating whether the current device allows data.
+         If true, this means that the device supports data connectivity through
+         the telephony network.
+         This can be overridden to false for devices that support voice and/or sms . -->
+    <bool name="config_mobile_data_capable">false</bool>
+
+    <!-- The name of the package that will hold the system contacts role. -->
+    <string name="config_systemContacts" translatable="false">com.android.tv.frameworkpackagestubs</string>
+
+    <!-- ComponentName of the default dream (Settings.Secure.DEFAULT_SCREENSAVER_COMPONENT) -->
+    <string name="config_dreamsDefaultComponent" translatable="false">com.android.dreams.basic/com.android.dreams.basic.Colors</string>
+
+    <!-- Override the DPad detection behavior for configuration purposes -->
     <bool name="config_hasPermanentDpad">true</bool>
 
+    <!-- Determines whether recent tasks are provided to the user. Default device has recents
+         property. If this is false, then the following recents config flags are ignored. -->
+    <bool name="config_hasRecents">false</bool>
+
+    <!-- Whether to start in touch mode -->
     <bool name="config_defaultInTouchMode">false</bool>
 
-    <!-- Launcher customization requires AppWidgetService, but otherwise
+    <!-- Allow the gesture to double tap the power button twice to start the camera while the device
+         is non-interactive. -->
+    <bool name="config_cameraDoubleTapPowerGestureEnabled">false</bool>
+
+    <!-- Inactivity threshold (in milliseconds) used in JobScheduler. JobScheduler will consider
+         the device to be "idle" after being inactive for this long. -->
+    <integer name="config_jobSchedulerInactivityIdleThreshold">300000</integer>
+
+    <!-- True if the device supports split screen as a form of multi-window. -->
+    <bool name="config_supportsSplitScreenMultiWindow">false</bool>
+
+    <!-- True if the device supports system decorations on secondary displays. -->
+    <bool name="config_supportsSystemDecorsOnSecondaryDisplays">false</bool>
+
+    <!-- True if the device requires AppWidgetService even if it does not have
+         the PackageManager.FEATURE_APP_WIDGETS feature.
+         TV launcher customization requires AppWidgetService, but otherwise
          home screen widgets are not supported on TV -->
     <bool name="config_enableAppWidgetService">true</bool>
 
+    <!-- Maximum size, specified in pixels, to restrain the display space width to. Height and
+         density will be scaled accordingly to maintain aspect ratio. A value of 0 indicates no
+         constraint will be enforced.
+         ATV limits the UI graphics width to 1920 because higher resolution is unnecessary and causes
+         too much overhead on the GPU for Android TV devices. -->
+    <integer name="config_maxUiWidth">1920</integer>
+
+    <!-- Whether the device supports quick settings and its associated APIs -->
+    <bool name="config_quickSettingsSupported">false</bool>
+
     <!-- Whether to keep background restricted profiles running after exiting. If set to false,
          restricted profiles may be put into stopped state as soon as the switch back to primary
          happens.
          Can be overridden with android.provider.Settings.Global.KEEP_PROFILE_IN_BACKGROUND. -->
     <bool name="config_keepRestrictedProfilesInBackground">false</bool>
 
-    <!-- Enable doze mode -->
-    <bool name="config_enableAutoPowerModes">true</bool>
-    <bool name="config_autoPowerModeUseMotionSensor">false</bool>
-
-    <!-- True if the device supports split screen as a form of multi-window. -->
-    <bool name="config_supportsSplitScreenMultiWindow">false</bool>
-
-    <!-- Whether the device supports quick settings and its associated APIs -->
-    <bool name="config_quickSettingsSupported">false</bool>
-
-    <!-- True if the device supports system decorations on secondary displays. -->
-    <bool name="config_supportsSystemDecorsOnSecondaryDisplays">false</bool>
-
-    <!-- Enable assistant to show in front of the dream/screensaver. -->
+    <!-- Set to true to make assistant show in front of the dream/screensaver. -->
     <bool name="config_assistantOnTopOfDream">true</bool>
 
-    <!-- Maximum size, specified in pixels, to restrain the display space width to. Height and
-         density will be scaled accordingly to maintain aspect ratio. A value of 0 indicates no
-         constraint will be enforced.
-         We limit the UI graphics width to 1920 because higher resolution is unnecessary and causes
-         too much overhead on the GPU for Android TV devices. -->
-    <integer name="config_maxUiWidth">1920</integer>
-
-    <!-- If true, enables verification of the lockscreen credential in the factory reset protection
-        flow. This should be true if gatekeeper / weaver credentials can still be checked after a
-        factory reset. -->
-    <bool name="config_enableCredentialFactoryResetProtection">false</bool>
-
-    <!-- Control the behavior when the user long presses the power button.
-        0 - Nothing
-        1 - Global actions menu
-        2 - Power off (with confirmation)
-        3 - Power off (without confirmation)
-        4 - Go to voice assist
-        5 - Go to assistant (Settings.Secure.ASSISTANT -->
-    <integer name="config_longPressOnPowerBehavior">3</integer>
-
-    <!-- Whether the time zone detection feature is enabled. Disabled for ATV devices. -->
-    <bool name="config_enableGeolocationTimeZoneDetection" translatable="false">false</bool>
-
-    <!-- Whether this device is supporting the microphone toggle -->
-    <bool name="config_supportsMicToggle">true</bool>
-    <!-- Whether this device is supporting the camera toggle -->
-    <bool name="config_supportsCamToggle">true</bool>
-
-    <!-- ComponentName of the default dream (Settings.Secure.DEFAULT_SCREENSAVER_COMPONENT) -->
-    <string name="config_dreamsDefaultComponent" translatable="false">com.android.dreams.basic/com.android.dreams.basic.Colors</string>
-
     <!-- If true, Views will declare they prefer to be kept clear from overlays when focused. -->
     <bool name="config_preferKeepClearForFocus">true</bool>
 
+    <!-- Whether this device is supporting the software microphone toggle -->
+    <bool name="config_supportsMicToggle">true</bool>
+    <!-- Whether this device is supporting the camera toggle -->
+    <bool name="config_supportsCamToggle">true</bool>
+
     <!-- Determines whether SafetyCenter feature is enabled.
          SafetyCenter is not yet supported on TV. -->
     <bool name="config_enableSafetyCenter">false</bool>
 
-    <!-- The name of the package that will hold the system contacts role. -->
-    <string name="config_systemContacts" translatable="false">com.android.tv.frameworkpackagestubs</string>
+    <!-- Flag indicating whether seamless refresh rate switching is supported by a device. -->
+    <bool name="config_supportsSeamlessRefreshRateSwitching">false</bool>
+
+    <!-- If a location should be pre-fetched when going into device idle. -->
+    <bool name="config_autoPowerModePrefetchLocation">false</bool>
+
+    <!-- Whether Low Power Standby is supported and can be enabled. -->
+    <bool name="config_lowPowerStandbySupported">true</bool>
+
+    <!-- If supported, whether Low Power Standby is enabled by default. -->
+    <bool name="config_lowPowerStandbyEnabledByDefault">true</bool>
 </resources>
diff --git a/overlay/TvFrameworkOverlay/res/values/config_device_idle.xml b/overlay/TvFrameworkOverlay/res/values/config_device_idle.xml
new file mode 100644
index 0000000..98d5a37
--- /dev/null
+++ b/overlay/TvFrameworkOverlay/res/values/config_device_idle.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2022, 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.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+     for different hardware and product builds.  Do not translate.
+
+     NOTE: The naming convention is "config_camelCaseValue". Some legacy
+     entries do not follow the convention, but all new entries should. -->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Default for DeviceIdleController.Constants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET -->
+    <integer name="device_idle_light_idle_maintenance_min_budget_ms">30000</integer>
+
+    <!-- Default for DeviceIdleController.Constants.LIGHT_IDLE_MAINTENANCE_MAX_BUDGET -->
+    <integer name="device_idle_light_idle_maintenance_max_budget_ms">180000</integer>
+
+    <!-- Default for DeviceIdleController.Constants.INACTIVE_TIMEOUT -->
+    <integer name="device_idle_inactive_to_ms">600000</integer>
+
+    <!-- Default for DeviceIdleController.Constants.LOCATING_TIMEOUT -->
+    <integer name="device_idle_locating_to_ms">100</integer>
+
+    <!-- Default for DeviceIdleController.Constants.IDLE_AFTER_INACTIVE_TIMEOUT -->
+    <integer name="device_idle_idle_after_inactive_to_ms">0</integer>
+
+    <!-- Default for DeviceIdleController.Constants.IDLE_TIMEOUT -->
+    <integer name="device_idle_idle_to_ms">5400000</integer>
+
+    <!-- Default for DeviceIdleController.Constants.MIN_TIME_TO_ALARM -->
+    <integer name="device_idle_min_time_to_alarm_ms">3600</integer>
+
+    <!-- Default for DeviceIdleController.Constants.MAX_TEMP_APP_ALLOWLIST_DURATION_MS -->
+    <integer name="device_idle_max_temp_app_allowlist_duration_ms">120000</integer>
+</resources>
diff --git a/overlay/TvNetworkStackOverlay/Android.bp b/overlay/TvNetworkStackOverlay/Android.bp
new file mode 100644
index 0000000..0a30622
--- /dev/null
+++ b/overlay/TvNetworkStackOverlay/Android.bp
@@ -0,0 +1,18 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+runtime_resource_overlay {
+    name: "TvNetworkStackOverlay",
+    certificate: "platform",
+    resource_dirs: ["res"],
+    product_specific: true,
+    sdk_version: "current",
+}
+
+override_runtime_resource_overlay {
+    name: "TvInProcessNetworkStackOverlay",
+    base: "TvNetworkStackOverlay",
+    package_name: "com.android.tv.overlay.networkstack.inprocess",
+    target_package_name: "com.android.networkstack.inprocess",
+}
diff --git a/overlay/TvNetworkStackOverlay/AndroidManifest.xml b/overlay/TvNetworkStackOverlay/AndroidManifest.xml
new file mode 100644
index 0000000..c518f56
--- /dev/null
+++ b/overlay/TvNetworkStackOverlay/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<!--
+  Copyright (C) 2023 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.tv.overlay.networkstack"
+    android:versionCode="1"
+    android:versionName="1.0" >
+
+    <application android:hasCode="false" />
+
+    <overlay
+        android:targetPackage="com.android.networkstack"
+        android:targetName="NetworkStackConfig"
+        android:priority="0"
+        android:isStatic="true" />
+</manifest>
diff --git a/overlay/TvNetworkStackOverlay/res/values/config.xml b/overlay/TvNetworkStackOverlay/res/values/config.xml
new file mode 100644
index 0000000..eb90d88
--- /dev/null
+++ b/overlay/TvNetworkStackOverlay/res/values/config.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 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.
+-->
+
+<resources>
+    <!-- Increase the tolerance towards unresponsive neighbors on a Wi-Fi network. -->
+    <integer name="config_nud_steadystate_solicit_num">15</integer>
+    <integer name="config_nud_steadystate_solicit_interval">1000</integer>
+    <integer name="config_nud_postroaming_solicit_num">15</integer>
+    <integer name="config_nud_postroaming_solicit_interval">1000</integer>
+</resources>
diff --git a/overlay/TvWifiOverlay/Android.bp b/overlay/TvWifiOverlay/Android.bp
index 200b695..7499087 100644
--- a/overlay/TvWifiOverlay/Android.bp
+++ b/overlay/TvWifiOverlay/Android.bp
@@ -14,3 +14,10 @@
     product_specific: true,
     sdk_version: "current",
 }
+
+override_runtime_resource_overlay {
+    name: "TvWifiOverlayGoogle",
+    base: "TvWifiOverlay",
+    package_name: "com.google.android.tv.overlay.wifi.resources",
+    target_package_name: "com.google.android.wifi.resources",
+}
\ No newline at end of file
diff --git a/overlay/TvWifiOverlay/AndroidManifest.xml b/overlay/TvWifiOverlay/AndroidManifest.xml
index f0372be..9ea1edf 100644
--- a/overlay/TvWifiOverlay/AndroidManifest.xml
+++ b/overlay/TvWifiOverlay/AndroidManifest.xml
@@ -23,7 +23,7 @@
     <overlay
         android:targetPackage="com.android.wifi.resources"
         android:targetName="WifiCustomization"
-        android:priority="-1"
+        android:priority="0"
         android:isStatic="true" />
 
 </manifest>
diff --git a/overlay/TvWifiOverlay/res/values/config.xml b/overlay/TvWifiOverlay/res/values/config.xml
index d800492..679995b 100644
--- a/overlay/TvWifiOverlay/res/values/config.xml
+++ b/overlay/TvWifiOverlay/res/values/config.xml
@@ -20,4 +20,22 @@
          believe it's rare to have multiple Wifi networks configured whose
          quality changes over time. -->
     <bool name="config_wifi_framework_enable_associated_network_selection">false</bool>
+
+    <!-- Enable the Software PNO feature. If Hardware PNO is not enabled, the device is in
+    standby and Wifi is disconnected, periodic scans are performed according to the
+    following schedule:
+    1) config_wifiSwPnoMobilityStateTimerIterations iterations with initial interval provided by
+    the mobility status (see config_wifiMovingPnoScanIntervalMillis and
+    config_wifiStationaryPnoScanIntervalMillis for the default values respectively for moving and
+    stationary devices). At each iteration the interval is increased proportionally to the elapsed
+    iterations. The device is awakened even if currently in doze/idle mode.
+    2) config_wifiSwPnoFastTimerIterations iterations with initial interval provided by
+    config_wifiSwPnoFastTimerMs. The device is awakened even if currently in doze/idle mode.
+    3) config_wifiSwPnoSlowTimerIterations iterations with initial provided by
+    config_wifiSwPnoSlowTimerMs and a window of config_wifiSwPnoSlowTimerMargin.
+    Inside such time window, the device is not awakened to perform the scan. If a wakeup happens for
+    other reasons, the scan might be performed as well in order to avoid subsequent awakening.
+    If no spontaneous awakening happens at the end of the time window, the device is awakened to
+    perform the scan.-->
+    <bool translatable="false" name="config_wifiSwPnoEnabled">true</bool>
 </resources>
diff --git a/permissions/tv_core_hardware.xml b/permissions/tv_core_hardware.xml
index 565f72c..b914510 100644
--- a/permissions/tv_core_hardware.xml
+++ b/permissions/tv_core_hardware.xml
@@ -31,7 +31,7 @@
     <feature name="android.software.leanback_only" />
     <feature name="android.software.live_tv" />
     <feature name="android.software.picture_in_picture" notLowRam="true" />
-    <feature name="android.software.activities_on_secondary_displays" notLowRam="true" />
+    <feature name="android.software.expanded_picture_in_picture" notLowRam="true" />
     <feature name="android.software.voice_recognizers" />
     <feature name="android.software.input_methods" />
     <feature name="android.software.autofill" />
diff --git a/products/AndroidProducts.mk b/products/AndroidProducts.mk
index 110cf2f..390ed4f 100644
--- a/products/AndroidProducts.mk
+++ b/products/AndroidProducts.mk
@@ -32,10 +32,8 @@
 #
 
 PRODUCT_MAKEFILES := \
-    $(LOCAL_DIR)/aosp_tv_arm.mk \
     $(LOCAL_DIR)/aosp_tv_arm64.mk \
     $(LOCAL_DIR)/aosp_tv_x86.mk \
     $(LOCAL_DIR)/gsi_tv_arm.mk \
     $(LOCAL_DIR)/gsi_tv_arm64.mk \
-    $(LOCAL_DIR)/sdk_atv_armv7.mk \
     $(LOCAL_DIR)/sdk_atv_x86.mk
diff --git a/products/aosp_tv_arm.mk b/products/aosp_tv_arm.mk
deleted file mode 100644
index ca26028..0000000
--- a/products/aosp_tv_arm.mk
+++ /dev/null
@@ -1,86 +0,0 @@
-#
-# Copyright (C) 2020 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.
-#
-
-# Generate source.properties for image target
-PRODUCT_SDK_ADDON_SYS_IMG_SOURCE_PROP := \
-    device/google/atv/sdk/images_source.prop_template
-
-PRODUCT_USE_DYNAMIC_PARTITIONS := true
-
-# The system image of aosp_tv_arm-userdebug is a GSI for the devices with:
-# - ARM 32 bits user space
-# - 64 bits binder interface
-# - VNDK enforcement
-# - compatible property override enabled
-
-#
-# All components inherited here go to system image
-#
-$(call inherit-product, device/google/atv/products/atv_generic_system.mk)
-
-# Enable mainline checking for excat this product name
-ifeq (aosp_tv_arm,$(TARGET_PRODUCT))
-PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS := relaxed
-endif
-
-#
-# All components inherited here go to system_ext image
-#
-$(call inherit-product, device/google/atv/products/atv_system_ext.mk)
-# Packages required for ATV GSI
-PRODUCT_PACKAGES += \
-    TvProvision
-
-#
-# All components inherited here go to product image
-#
-$(call inherit-product, device/google/atv/products/atv_product.mk)
-# Packages required for ATV GSI
-PRODUCT_PACKAGES += \
-    LeanbackIME \
-    TvSampleLeanbackLauncher
-
-#
-# All components inherited here go to vendor image
-#
-$(call inherit-product, device/google/atv/products/atv_emulator_vendor.mk)
-$(call inherit-product-if-exists, device/generic/goldfish/arm32-vendor.mk)
-$(call inherit-product, $(SRC_TARGET_DIR)/board/generic_x86/device.mk)
-
-#
-# Special settings for GSI releasing
-#
-ifeq (aosp_tv_arm,$(TARGET_PRODUCT))
-$(call inherit-product, $(SRC_TARGET_DIR)/product/gsi_release.mk)
-PRODUCT_BUILD_SYSTEM_IMAGE := true
-PRODUCT_BUILD_SYSTEM_OTHER_IMAGE := false
-PRODUCT_BUILD_VENDOR_IMAGE := false
-PRODUCT_BUILD_PRODUCT_IMAGE := false
-PRODUCT_BUILD_PRODUCT_SERVICES_IMAGE := false
-PRODUCT_BUILD_ODM_IMAGE := false
-PRODUCT_BUILD_CACHE_IMAGE := false
-PRODUCT_BUILD_RAMDISK_IMAGE := true
-PRODUCT_BUILD_USERDATA_IMAGE := false
-PRODUCT_BUILD_BOOT_IMAGE := false
-PRODUCT_BUILD_DEBUG_BOOT_IMAGE := false
-PRODUCT_BUILD_VENDOR_BOOT_IMAGE := false
-PRODUCT_BUILD_DEBUG_VENDOR_BOOT_IMAGE := false
-endif
-
-PRODUCT_NAME := aosp_tv_arm
-PRODUCT_DEVICE := generic
-PRODUCT_BRAND := Android
-PRODUCT_MODEL := AOSP TV on ARM
diff --git a/products/atv_emulator_vendor.mk b/products/atv_emulator_vendor.mk
index 4b12dd3..4659e0b 100644
--- a/products/atv_emulator_vendor.mk
+++ b/products/atv_emulator_vendor.mk
@@ -18,11 +18,15 @@
 # TV emulator-related modules to PRODUCT_PACKAGES.
 #
 
-# ATV SDK is not designed to have a GNSS-receiver by default
 EMULATOR_VENDOR_NO_GNSS := true
+EMULATOR_VENDOR_NO_BIOMETRICS := true
+EMULATOR_VENDOR_NO_SENSORS := true
 
 $(call inherit-product, device/google/atv/products/atv_vendor.mk)
 
+# Sets HDMI CEC as Playback Device.
+PRODUCT_PROPERTY_OVERRIDES += ro.hdmi.device_type=4
+
 # need this for gles libraries to load properly
 # after moving to /vendor/lib/
 PRODUCT_PACKAGES += \
@@ -42,12 +46,8 @@
 
 PRODUCT_COPY_FILES += \
     device/generic/goldfish/data/etc/apns-conf.xml:$(TARGET_COPY_OUT_VENDOR)/etc/apns-conf.xml \
-    device/generic/goldfish/camera/media_codecs.xml:$(TARGET_COPY_OUT_VENDOR)/etc/media_codecs.xml \
-    frameworks/av/media/libstagefright/data/media_codecs_google_audio.xml:$(TARGET_COPY_OUT_VENDOR)/etc/media_codecs_google_audio.xml \
+    device/generic/goldfish/camera/media/media_codecs_google_tv.xml:${TARGET_COPY_OUT_VENDOR}/etc/media_codecs_google_tv.xml \
     frameworks/native/data/etc/android.hardware.ethernet.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.ethernet.xml \
-    frameworks/av/media/libstagefright/data/media_codecs_google_telephony.xml:$(TARGET_COPY_OUT_VENDOR)/etc/media_codecs_google_telephony.xml \
-    frameworks/av/media/libstagefright/data/media_codecs_google_tv.xml:$(TARGET_COPY_OUT_VENDOR)/etc/media_codecs_google_tv.xml \
-    frameworks/av/media/libstagefright/data/media_codecs_google_video.xml:$(TARGET_COPY_OUT_VENDOR)/etc/media_codecs_google_video.xml \
     hardware/libhardware_legacy/audio/audio_policy.conf:$(TARGET_COPY_OUT_VENDOR)/etc/audio_policy.conf
 
 # Exclude all non-default hardware features on ATV SDK.
@@ -56,7 +56,7 @@
     device/google/atv/permissions/tv_sdk_excluded_core_hardware.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/tv_sdk_excluded_core_hardware.xml
 
 # goldfish vendor partition configurations
-$(call inherit-product-if-exists, device/generic/goldfish/vendor.mk)
+$(call inherit-product-if-exists, device/generic/goldfish/64bitonly/product/vendor.mk)
 
 #watchdog tiggers reboot because location service is not
 #responding, disble it for now.
diff --git a/products/atv_generic_system.mk b/products/atv_generic_system.mk
index 7985ef8..d14f2a1 100644
--- a/products/atv_generic_system.mk
+++ b/products/atv_generic_system.mk
@@ -40,7 +40,7 @@
 # System libraries commonly depended on by things on the system_ext or product partitions.
 # These lists will be pruned periodically.
 PRODUCT_PACKAGES += \
-    android.hardware.wifi@1.0 \
+    android.hardware.wifi \
     libaudio-resampler \
     libaudiohal \
     libdrm \
@@ -60,10 +60,11 @@
 PRODUCT_PACKAGES_DEBUG += \
     avbctl \
     bootctl \
-    tinyplay \
     tinycap \
+    tinyhostless \
     tinymix \
     tinypcminfo \
+    tinyplay \
     update_engine_client
 
 PRODUCT_HOST_PACKAGES += \
diff --git a/products/atv_lowram_defaults.mk b/products/atv_lowram_defaults.mk
index 182e129..7ce3557 100644
--- a/products/atv_lowram_defaults.mk
+++ b/products/atv_lowram_defaults.mk
@@ -34,13 +34,6 @@
 # Do not generate libartd.
 PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD := false
 
-# Do not spin up a separate process for the network stack on low ram devices, use an in-process APK.
-ifneq ($(PRODUCT_IS_ATV_SDK),true)
-  PRODUCT_PACKAGES += InProcessNetworkStack
-  PRODUCT_PACKAGES += PlatformCaptivePortalLogin
-  PRODUCT_PACKAGES += com.android.tethering.inprocess
-endif
-
 # Strip the local variable table and the local variable type table to reduce
 # the size of the system image. This has no bearing on stack traces, but will
 # leave less information available via JDWP.
diff --git a/products/atv_product.mk b/products/atv_product.mk
index 40e80fa..4c38e84 100644
--- a/products/atv_product.mk
+++ b/products/atv_product.mk
@@ -17,14 +17,20 @@
 # a generic TV device.
 $(call inherit-product, $(SRC_TARGET_DIR)/product/media_product.mk)
 
-$(call inherit-product-if-exists, frameworks/base/data/sounds/AudioTv.mk)
-
 PRODUCT_PUBLIC_SEPOLICY_DIRS += device/google/atv/audio_proxy/sepolicy/public
 
 PRODUCT_PACKAGES += \
+    TvNetworkStackOverlay \
     TvFrameworkOverlay \
     TvSettingsProviderOverlay \
-    TvWifiOverlay
+    TvWifiOverlay \
+    SettingsIntelligence
+
+# Override com.android.* overlays with com.google.android.* overlays for mainline
+ifeq ($(PRODUCT_IS_ATV_MAINLINE), true)
+PRODUCT_PACKAGES += \
+    TvWifiOverlayGoogle
+endif
 
 PRODUCT_COPY_FILES += \
     device/google/atv/atv-component-overrides.xml:$(TARGET_COPY_OUT_PRODUCT)/etc/sysconfig/atv-component-overrides.xml
diff --git a/products/atv_system.mk b/products/atv_system.mk
index 5515f3e..84bfa68 100644
--- a/products/atv_system.mk
+++ b/products/atv_system.mk
@@ -47,7 +47,6 @@
 # From build/target/product/core.mk
 PRODUCT_PACKAGES += \
     BasicDreams \
-    Bluetooth \
     CalendarProvider \
     CaptivePortalLogin \
     CertInstaller \
@@ -65,6 +64,10 @@
     VpnDialogs \
     com.android.media.tv.remoteprovider
 
+# Use TV PackageInstaller
+PRODUCT_PACKAGES += \
+    PackageInstaller_tv
+
 # Device owner provisioning for devices defining device_admin
 PRODUCT_PACKAGES += \
     ManagedProvisioning
@@ -79,6 +82,7 @@
 PRODUCT_SUPPORTS_CAMERA ?= true
 ifeq ($(PRODUCT_SUPPORTS_CAMERA),true)
     PRODUCT_PACKAGES += cameraserver
+    PRODUCT_PACKAGES += CameraExtensionsProxy
 else
     # When cameraserver is not included, we need to configure Camera API to not
     # connect to it.
@@ -114,4 +118,4 @@
     device/google/atv/permissions/tv_core_hardware.xml:system/etc/permissions/tv_core_hardware.xml
 
 PRODUCT_COPY_FILES += \
-    frameworks/av/media/libeffects/data/audio_effects.conf:system/etc/audio_effects.conf
+    frameworks/av/media/libeffects/data/audio_effects.xml:system/etc/audio_effects.xml
diff --git a/products/gsi_tv_base.mk b/products/gsi_tv_base.mk
index acda9fb..d9576ab 100644
--- a/products/gsi_tv_base.mk
+++ b/products/gsi_tv_base.mk
@@ -45,6 +45,8 @@
 PRODUCT_PACKAGES += \
     LeanbackIME \
     TvSampleLeanbackLauncher
+# Specify product type
+PRODUCT_CHARACTERISTICS := tv
 
 #
 # Special settings for GSI releasing
diff --git a/products/sdk_atv_armv7.mk b/products/sdk_atv_arm64.mk
similarity index 62%
rename from products/sdk_atv_armv7.mk
rename to products/sdk_atv_arm64.mk
index e161a3b..f887f8b 100644
--- a/products/sdk_atv_armv7.mk
+++ b/products/sdk_atv_arm64.mk
@@ -1,5 +1,5 @@
 #
-# Copyright (C) 2014 The Android Open Source Project
+# Copyright (C) 2023 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.
@@ -13,21 +13,9 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
-
 PRODUCT_IS_ATV_SDK := true
 
-QEMU_USE_SYSTEM_EXT_PARTITIONS := true
-
-$(call inherit-product, device/google/atv/products/aosp_tv_arm.mk)
-
-# keep this apk for sdk targets for now
-PRODUCT_PACKAGES += \
-    EmulatorSmokeTests
-
 # Overrides
 PRODUCT_BRAND := Android
-PRODUCT_NAME := sdk_atv_armv7
-PRODUCT_DEVICE := generic
-
-PRODUCT_PRODUCT_PROPERTIES += \
-    ro.oem.key1=ATV00100020
+PRODUCT_NAME := sdk_atv_arm64
+PRODUCT_DEVICE := generic_arm64
diff --git a/products/sdk_atv_x86.mk b/products/sdk_atv_x86.mk
index 57555ca..dde3edb 100644
--- a/products/sdk_atv_x86.mk
+++ b/products/sdk_atv_x86.mk
@@ -15,8 +15,6 @@
 #
 PRODUCT_IS_ATV_SDK := true
 
-QEMU_USE_SYSTEM_EXT_PARTITIONS := true
-
 $(call inherit-product, device/google/atv/products/aosp_tv_x86.mk)
 
 # keep this apk for sdk targets for now
diff --git a/sdk/images_armeabi-v7a_source.prop_template b/sdk/images_armeabi-v7a_source.prop_template
index 3c6a41e..d38fe89 100644
--- a/sdk/images_armeabi-v7a_source.prop_template
+++ b/sdk/images_armeabi-v7a_source.prop_template
@@ -1,6 +1,6 @@
 Pkg.Desc=Android SDK Platform ${PLATFORM_VERSION}
 Pkg.UserSrc=false
-Pkg.Revision=3
+Pkg.Revision=1
 AndroidVersion.ApiLevel=${PLATFORM_SDK_VERSION}
 AndroidVersion.CodeName=${PLATFORM_VERSION_CODENAME}
 SystemImage.Abi=armeabi-v7a
diff --git a/sdk/images_source.prop_template b/sdk/images_source.prop_template
index a05bc28..8972e78 100644
--- a/sdk/images_source.prop_template
+++ b/sdk/images_source.prop_template
@@ -1,6 +1,6 @@
 Pkg.Desc=Android SDK Platform ${PLATFORM_VERSION}
 Pkg.UserSrc=false
-Pkg.Revision=3
+Pkg.Revision=1
 Pkg.Dependencies=emulator#28.1.6
 AndroidVersion.ApiLevel=${PLATFORM_SDK_VERSION}
 AndroidVersion.CodeName=${PLATFORM_VERSION_CODENAME}