Snap for 8564071 from 958a32cdadd093930bda607e80f1bd809b97019a to mainline-wifi-release

Change-Id: I300407825294a6f8287fb911c3e33e91025aeffd
diff --git a/Android.bp b/Android.bp
index 770a6b1..099c174 100644
--- a/Android.bp
+++ b/Android.bp
@@ -14,6 +14,10 @@
 // limitations under the License.
 //
 
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 android_app {
     name: "CarRadioApp",
 
@@ -27,13 +31,13 @@
 
     platform_apis: true,
 
-    required: ["privapp_whitelist_com.android.car.radio"],
+    required: ["allowed_privapp_com.android.car.radio"],
 
     certificate: "platform",
 
     privileged: true,
 
-    libs: ["android.car"],
+    libs: ["android.car-system-stubs"],
 
     static_libs: [
         "androidx.lifecycle_lifecycle-livedata",
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index eccbf94..cdae76d 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -52,13 +52,13 @@
                 <category android:name="android.intent.category.DEFAULT" />
                 <category android:name="android.intent.category.LAUNCHER" />
                 <category android:name="android.intent.category.LAUNCHER_APP" />
-                <category android:name="android.intent.category.APP_MUSIC" />
             </intent-filter>
         </activity>
 
         <!-- RadioAppService is set to be executed in a separate process to make sure the reference
              app supports it. It's less optimal though, so it might be desirable to turn it off in
              production app. -->
+        <!-- Keep in sync with RadioAppService#getMediaSourceComp. -->
         <service
             android:name=".service.RadioAppService"
             android:exported="true"
diff --git a/src/com/android/car/radio/BrowseFragment.java b/src/com/android/car/radio/BrowseFragment.java
index f81fc1d..3d53620 100644
--- a/src/com/android/car/radio/BrowseFragment.java
+++ b/src/com/android/car/radio/BrowseFragment.java
@@ -26,23 +26,25 @@
 
 import androidx.fragment.app.Fragment;
 import androidx.recyclerview.widget.LinearLayoutManager;
-import androidx.recyclerview.widget.RecyclerView;
 
 import com.android.car.broadcastradio.support.Program;
 import com.android.car.radio.storage.RadioStorage;
+import com.android.car.radio.util.Log;
 import com.android.car.ui.baselayout.Insets;
 import com.android.car.ui.baselayout.InsetsChangedListener;
+import com.android.car.ui.recyclerview.CarUiRecyclerView;
 
 /**
  * Fragment that shows all browseable radio stations from background scan
  */
 public class BrowseFragment extends Fragment implements InsetsChangedListener {
+    private static final String TAG = "BcRadioApp.BrwFrg";
 
     private RadioController mRadioController;
     private BrowseAdapter mBrowseAdapter;
     private RadioStorage mRadioStorage;
     private View mRootView;
-    private RecyclerView mBrowseList;
+    private CarUiRecyclerView mBrowseList;
 
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container,
@@ -75,8 +77,13 @@
     @Override
     public void setUserVisibleHint(boolean isVisibleToUser) {
         super.setUserVisibleHint(isVisibleToUser);
-        if (isVisibleToUser) {
+
+        if (!isVisibleToUser) return;
+
+        try {
             mRadioController.setSkipMode(SkipMode.BROWSE);
+        } catch (IllegalStateException e) {
+            Log.e(TAG, "Can't set skip mode", e);
         }
     }
 
diff --git a/src/com/android/car/radio/DisplayController.java b/src/com/android/car/radio/DisplayController.java
index 14b131e..b2a09b5 100644
--- a/src/com/android/car/radio/DisplayController.java
+++ b/src/com/android/car/radio/DisplayController.java
@@ -50,7 +50,6 @@
 
     private final Context mContext;
 
-    private final View mToolbar;
     private final View mViewpager;
     private final TextView mStatusMessage;
     private final TextView mChannel;
@@ -85,7 +84,6 @@
             @NonNull RadioController radioController) {
         mContext = Objects.requireNonNull(activity);
 
-        mToolbar = activity.findViewById(R.id.toolbar);
         mViewpager = activity.findViewById(R.id.viewpager);
         mStatusMessage = activity.findViewById(R.id.status_message);
         mChannel = activity.findViewById(R.id.station_channel);
@@ -144,9 +142,6 @@
         if (mFavoriteButton != null) {
             mFavoriteButton.setVisibility(enabled ? View.VISIBLE : View.GONE);
         }
-        if (mToolbar != null) {
-            mToolbar.setVisibility(enabled ? View.VISIBLE : View.INVISIBLE);
-        }
         if (mViewpager != null) {
             mViewpager.setVisibility(enabled ? View.VISIBLE : View.GONE);
         }
diff --git a/src/com/android/car/radio/FavoritesFragment.java b/src/com/android/car/radio/FavoritesFragment.java
index 218a318..f4c2f43 100644
--- a/src/com/android/car/radio/FavoritesFragment.java
+++ b/src/com/android/car/radio/FavoritesFragment.java
@@ -26,22 +26,24 @@
 
 import androidx.fragment.app.Fragment;
 import androidx.recyclerview.widget.LinearLayoutManager;
-import androidx.recyclerview.widget.RecyclerView;
 
 import com.android.car.broadcastradio.support.Program;
 import com.android.car.radio.storage.RadioStorage;
+import com.android.car.radio.util.Log;
 import com.android.car.ui.baselayout.Insets;
 import com.android.car.ui.baselayout.InsetsChangedListener;
+import com.android.car.ui.recyclerview.CarUiRecyclerView;
 
 /**
  * Fragment that shows a list of all the current favorite radio stations
  */
 public class FavoritesFragment extends Fragment implements InsetsChangedListener {
+    private static final String TAG = "BcRadioApp.FavFrg";
 
     private RadioController mRadioController;
     private BrowseAdapter mBrowseAdapter;
     private RadioStorage mRadioStorage;
-    private RecyclerView mBrowseList;
+    private CarUiRecyclerView mBrowseList;
 
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container,
@@ -72,11 +74,17 @@
     @Override
     public void setUserVisibleHint(boolean isVisibleToUser) {
         super.setUserVisibleHint(isVisibleToUser);
+
         if (!isVisibleToUser && mBrowseAdapter != null) {
             mBrowseAdapter.removeFormerFavorites();
         }
+
         if (isVisibleToUser) {
-            mRadioController.setSkipMode(SkipMode.FAVORITES);
+            try {
+                mRadioController.setSkipMode(SkipMode.FAVORITES);
+            } catch (IllegalStateException e) {
+                Log.e(TAG, "Can't set skip mode", e);
+            }
         }
     }
 
diff --git a/src/com/android/car/radio/ManualTunerFragment.java b/src/com/android/car/radio/ManualTunerFragment.java
index 9fe6bcc..b668531 100644
--- a/src/com/android/car/radio/ManualTunerFragment.java
+++ b/src/com/android/car/radio/ManualTunerFragment.java
@@ -27,6 +27,7 @@
 import androidx.fragment.app.Fragment;
 
 import com.android.car.radio.bands.ProgramType;
+import com.android.car.radio.util.Log;
 import com.android.car.ui.baselayout.Insets;
 import com.android.car.ui.baselayout.InsetsChangedListener;
 
@@ -34,6 +35,7 @@
  * Fragment that allows tuning to a specific frequency using a keypad
  */
 public class ManualTunerFragment extends Fragment implements InsetsChangedListener {
+    private static final String TAG = "BcRadioApp.TunFrg";
 
     private ManualTunerController mController;
     private RadioController mRadioController;
@@ -53,11 +55,16 @@
         super.setUserVisibleHint(isVisibleToUser);
 
         if (!isVisibleToUser) return;
+
         ProgramInfo current = mRadioController.getCurrentProgram().getValue();
-        if (current == null) return;
-        mController.switchProgramType(ProgramType.fromSelector(current.getSelector()));
-        if (isVisibleToUser) {
+        if (current != null) {
+            mController.switchProgramType(ProgramType.fromSelector(current.getSelector()));
+        }
+
+        try {
             mRadioController.setSkipMode(SkipMode.TUNE);
+        } catch (IllegalStateException e) {
+            Log.e(TAG, "Can't set skip mode", e);
         }
     }
 
diff --git a/src/com/android/car/radio/RadioActivity.java b/src/com/android/car/radio/RadioActivity.java
index 73582b5..da8eb30 100644
--- a/src/com/android/car/radio/RadioActivity.java
+++ b/src/com/android/car/radio/RadioActivity.java
@@ -16,12 +16,9 @@
 
 package com.android.car.radio;
 
-import static android.car.media.CarMediaManager.MEDIA_SOURCE_MODE_BROWSE;
-
 import static com.android.car.ui.core.CarUi.requireToolbar;
 import static com.android.car.ui.toolbar.Toolbar.State.HOME;
 
-import android.car.Car;
 import android.content.Intent;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
@@ -32,8 +29,9 @@
 import androidx.viewpager.widget.ViewPager;
 
 import com.android.car.media.common.source.MediaSource;
-import com.android.car.media.common.source.MediaSourceViewModel;
+import com.android.car.media.common.source.MediaTrampolineHelper;
 import com.android.car.radio.bands.ProgramType;
+import com.android.car.radio.service.RadioAppService;
 import com.android.car.radio.util.Log;
 import com.android.car.ui.baselayout.Insets;
 import com.android.car.ui.baselayout.InsetsChangedListener;
@@ -67,6 +65,9 @@
     private ToolbarController mToolbar;
     private RadioPagerAdapter mRadioPagerAdapter;
 
+    private boolean mUseSourceLogoForAppSelector;
+    private MediaTrampolineHelper mMediaTrampoline;
+
     @Override
     public void onCarUiInsetsChanged(Insets insets) {
         // This InsetsChangedListener is just a marker that we will later handle
@@ -82,6 +83,8 @@
 
         setContentView(R.layout.radio_activity);
 
+        mMediaTrampoline = new MediaTrampolineHelper(this);
+
         mRadioController = new RadioController(this);
         mRadioController.getCurrentProgram().observe(this, info -> {
             ProgramType programType = ProgramType.fromSelector(info.getSelector());
@@ -96,25 +99,20 @@
         ViewPager viewPager = findViewById(R.id.viewpager);
         viewPager.setAdapter(mRadioPagerAdapter);
 
+        mUseSourceLogoForAppSelector =
+                getResources().getBoolean(R.bool.use_media_source_logo_for_app_selector);
+
         mToolbar = requireToolbar(this);
         mToolbar.setState(HOME);
         mToolbar.setTitle(R.string.app_name);
-        mToolbar.setLogo(R.drawable.logo_fm_radio);
+        if (!mUseSourceLogoForAppSelector) {
+            mToolbar.setLogo(R.drawable.logo_fm_radio);
+        }
         mToolbar.registerOnTabSelectedListener(t ->
-                viewPager.setCurrentItem(mToolbar.getTabLayout().getTabPosition(t)));
+                viewPager.setCurrentItem(mToolbar.getTabPosition(t)));
 
         updateMenuItems();
         setupTabsWithViewPager(viewPager);
-
-        MediaSourceViewModel model =
-                MediaSourceViewModel.get(getApplication(), MEDIA_SOURCE_MODE_BROWSE);
-        model.getPrimaryMediaSource().observe(this, source -> {
-            if (source != null) {
-                // Always go through the trampoline activity to keep all the dispatching logic
-                // there.
-                startActivity(new Intent(Car.CAR_INTENT_ACTION_MEDIA_TEMPLATE));
-            }
-        });
     }
 
     @Override
@@ -129,6 +127,34 @@
     }
 
     @Override
+    protected void onNewIntent(Intent intent) {
+        super.onNewIntent(intent);
+        setIntent(intent); // getIntent() should always return the most recent
+
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "onNewIntent: " + intent);
+        }
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        Intent intent = getIntent();
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "onResume intent: " + intent);
+        }
+
+        if (intent != null) {
+            mMediaTrampoline.setLaunchedMediaSource(RadioAppService.getMediaSourceComp(this));
+
+            // Mark the intent as consumed so that coming back from the media app selector doesn't
+            // set the source again.
+            setIntent(null);
+        }
+    }
+
+    @Override
     protected void onStop() {
         super.onStop();
 
@@ -198,7 +224,9 @@
 
         Intent appSelectorIntent = MediaSource.getSourceSelectorIntent(this, false);
         MenuItem appSelectorMenuItem = MenuItem.builder(this)
-                .setIcon(R.drawable.ic_app_switch)
+                .setIcon(mUseSourceLogoForAppSelector
+                        ? R.drawable.logo_fm_radio : R.drawable.ic_app_switch)
+                .setTinted(!mUseSourceLogoForAppSelector)
                 .setOnClickListener(m -> startActivity(appSelectorIntent))
                 .build();
 
diff --git a/src/com/android/car/radio/audio/AudioStreamController.java b/src/com/android/car/radio/audio/AudioStreamController.java
index c29811f..2b92a9b 100644
--- a/src/com/android/car/radio/audio/AudioStreamController.java
+++ b/src/com/android/car/radio/audio/AudioStreamController.java
@@ -21,6 +21,7 @@
 import android.media.AudioFocusRequest;
 import android.media.AudioManager;
 import android.media.session.PlaybackState;
+import android.util.IndentingPrintWriter;
 
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
@@ -80,7 +81,7 @@
      *
      * It may be ducked, transiently lost or delayed.
      */
-    private boolean mHasSomeFocus = false;
+    private boolean mHasSomeFocus;
 
     private int mCurrentPlaybackState = PlaybackState.STATE_NONE;
     private Object mTuningToken;
@@ -231,6 +232,7 @@
      * @return true, if request has succeeded (maybe delayed)
      */
     public boolean requestMuted(boolean muted) {
+        Log.v(TAG, "requestMuted(" + muted + ")");
         synchronized (mLock) {
             if (muted) {
                 if (mTuningToken == null) {
@@ -272,4 +274,19 @@
             }
         }
     }
+
+    /**
+     * Dumps the current audio stream controller state
+     */
+    public void dump(IndentingPrintWriter writer) {
+        writer.println("AudioStreamController");
+        writer.increaseIndent();
+        synchronized (mLock) {
+            writer.printf("Focus Request: %s\n", mGainFocusReq);
+            writer.printf("Has Some Focus: %b\n", mHasSomeFocus);
+            writer.printf("PlayBack State: %d\n", mCurrentPlaybackState);
+            writer.printf("Is Tuning Token Available: %s\n", mTuningToken != null);
+        }
+        writer.decreaseIndent();
+    }
 }
diff --git a/src/com/android/car/radio/media/TunerSession.java b/src/com/android/car/radio/media/TunerSession.java
index 0cda1f3..3d9c70c 100644
--- a/src/com/android/car/radio/media/TunerSession.java
+++ b/src/com/android/car/radio/media/TunerSession.java
@@ -149,6 +149,7 @@
 
         @Override
         public void onPlay() {
+            mAppService.tuneToDefaultIfNeeded();
             mAppService.setMuted(false);
         }
 
diff --git a/src/com/android/car/radio/service/IRadioAppService.aidl b/src/com/android/car/radio/service/IRadioAppService.aidl
index 6ece15a..66f9211 100644
--- a/src/com/android/car/radio/service/IRadioAppService.aidl
+++ b/src/com/android/car/radio/service/IRadioAppService.aidl
@@ -95,4 +95,9 @@
      * Returns current region config (like frequency ranges for AM/FM).
      */
     RegionConfig getRegionConfig();
+
+    /**
+     * Tunes to the previously selected program or the default channel.
+     */
+    void tuneToDefaultIfNeeded();
 }
diff --git a/src/com/android/car/radio/service/RadioAppService.java b/src/com/android/car/radio/service/RadioAppService.java
index 44342d5..e906bcd 100644
--- a/src/com/android/car/radio/service/RadioAppService.java
+++ b/src/com/android/car/radio/service/RadioAppService.java
@@ -18,6 +18,8 @@
 
 import static com.android.car.radio.util.Remote.tryExec;
 
+import android.content.ComponentName;
+import android.content.Context;
 import android.content.Intent;
 import android.hardware.radio.ProgramList;
 import android.hardware.radio.ProgramSelector;
@@ -30,7 +32,9 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.service.media.MediaBrowserService;
+import android.util.IndentingPrintWriter;
 
+import androidx.annotation.GuardedBy;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.lifecycle.Lifecycle;
@@ -68,6 +72,11 @@
     public static String ACTION_APP_SERVICE = "com.android.car.radio.ACTION_APP_SERVICE";
     private static final long PROGRAM_LIST_RATE_LIMITING = 1000;
 
+    /** Returns the {@link ComponentName} that represents this {@link MediaBrowserService}. */
+    public static @NonNull ComponentName getMediaSourceComp(Context context) {
+        return new ComponentName(context, RadioAppService.class);
+    }
+
     private final Object mLock = new Object();
     private final LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);
     private final List<IRadioAppCallback> mRadioAppCallbacks = new ArrayList<>();
@@ -85,10 +94,13 @@
     private TunerSession mMediaSession;
 
     // current observables state for newly bound IRadioAppCallbacks
+    @GuardedBy("mLock")
     private ProgramInfo mCurrentProgram = null;
+    @GuardedBy("mLock")
     private int mCurrentPlaybackState = PlaybackState.STATE_NONE;
+    @GuardedBy("mLock")
     private long mLastProgramListPush;
-
+    @GuardedBy("mLock")
     private RegionConfig mRegionConfigCache;
 
     private SkipController mSkipController;
@@ -132,14 +144,13 @@
             mProgramList.addOnCompleteListener(this::pushProgramListUpdate);
         }
 
-        tuneToDefault(null);
-        mAudioStreamController.requestMuted(false);
-
         mLifecycleRegistry.markState(Lifecycle.State.CREATED);
     }
 
     @Override
     public int onStartCommand(Intent intent, int flags, int startId) {
+        Log.d(TAG, "onStartCommand intent [%s] flags[%d] startId[%d]",
+                intent.toString(), flags, startId);
         mLifecycleRegistry.markState(Lifecycle.State.STARTED);
         if (BrowseTree.ACTION_PLAY_BROADCASTRADIO.equals(intent.getAction())) {
             Log.i(TAG, "Executing general play radio intent");
@@ -153,6 +164,7 @@
 
     @Override
     public IBinder onBind(Intent intent) {
+        Log.i(TAG, "onBind intent[" + intent + "]");
         mLifecycleRegistry.markState(Lifecycle.State.STARTED);
         if (mRadioTuner == null) return null;
         if (ACTION_APP_SERVICE.equals(intent.getAction())) {
@@ -186,6 +198,7 @@
     }
 
     private void onPlaybackStateChanged(int newState) {
+        Log.d(TAG, "onPlaybackStateChanged new state [%d]", newState);
         synchronized (mLock) {
             mCurrentPlaybackState = newState;
             for (IRadioAppCallback callback : mRadioAppCallbacks) {
@@ -195,6 +208,7 @@
     }
 
     private void onProgramListChanged() {
+        if (mProgramList == null) return;
         synchronized (mLock) {
             if (SystemClock.elapsedRealtime() - mLastProgramListPush > PROGRAM_LIST_RATE_LIMITING) {
                 pushProgramListUpdate();
@@ -203,6 +217,7 @@
     }
 
     private void pushProgramListUpdate() {
+        if (mProgramList == null) return;
         List<ProgramInfo> plist = mProgramList.toList();
 
         synchronized (mLock) {
@@ -244,8 +259,9 @@
                 mAudioStreamController = null;
             }
             if (mProgramList != null) {
-                mProgramList.close();
+                ProgramList oldList = mProgramList;
                 mProgramList = null;
+                oldList.close();
             }
             if (mRadioTuner != null) {
                 mRadioTuner.close();
@@ -279,10 +295,25 @@
 
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        if (mSkipController != null) {
-            pw.println("SkipController:"); mSkipController.dump(pw, "  ");
-        } else {
-            pw.println("no SkipController");
+        try (IndentingPrintWriter writer = new IndentingPrintWriter(pw)) {
+            pw.println("RadioAppService:");
+            writer.increaseIndent();
+            if (mSkipController != null) {
+                writer.increaseIndent();
+                mSkipController.dump(writer);
+                writer.decreaseIndent();
+            } else {
+                pw.println("No SkipController");
+            }
+
+            if (mAudioStreamController != null) {
+                writer.increaseIndent();
+                mAudioStreamController.dump(writer);
+                writer.decreaseIndent();
+            } else {
+                pw.println("No AudioStreamController");
+            }
+            writer.decreaseIndent();
         }
     }
 
@@ -371,6 +402,21 @@
         }
 
         @Override
+        public void tuneToDefaultIfNeeded() {
+            synchronized (mLock) {
+                if (mRadioTuner == null) {
+                    throw new IllegalStateException("Tuner session is closed");
+                }
+
+                if (mCurrentPlaybackState != PlaybackState.STATE_NONE) {
+                    return;
+                }
+            }
+
+            tuneToDefault(null);
+        }
+
+        @Override
         public void switchBand(ProgramType band) {
             tuneToDefault(band);
         }
diff --git a/src/com/android/car/radio/service/RadioAppServiceWrapper.java b/src/com/android/car/radio/service/RadioAppServiceWrapper.java
index 154a408..cfd07af 100644
--- a/src/com/android/car/radio/service/RadioAppServiceWrapper.java
+++ b/src/com/android/car/radio/service/RadioAppServiceWrapper.java
@@ -365,6 +365,13 @@
     }
 
     /**
+     * Tunes to the previously selected program or the default channel.
+     */
+    public void tuneToDefaultIfNeeded() {
+        callService(service -> service.tuneToDefaultIfNeeded());
+    }
+
+    /**
      * Tune to a default channel of a given program type (band).
      *
      * Usually, this means tuning to the recently listened program of a given band.
diff --git a/src/com/android/car/radio/service/SkipController.java b/src/com/android/car/radio/service/SkipController.java
index 574399b..5df31cb 100644
--- a/src/com/android/car/radio/service/SkipController.java
+++ b/src/com/android/car/radio/service/SkipController.java
@@ -16,6 +16,7 @@
 package com.android.car.radio.service;
 
 import android.os.RemoteException;
+import android.util.IndentingPrintWriter;
 
 import androidx.annotation.GuardedBy;
 import androidx.annotation.NonNull;
@@ -26,7 +27,6 @@
 import com.android.car.radio.SkipMode;
 import com.android.car.radio.util.Log;
 
-import java.io.PrintWriter;
 import java.util.List;
 
 /**
@@ -41,13 +41,13 @@
 
     private final IRadioAppService.Stub mService;
 
-    @GuardedBy("mlock")
+    @GuardedBy("mLock")
     private List<Program> mFavorites;
 
-    @GuardedBy("mlock")
+    @GuardedBy("mLock")
     private int mCurrentIndex;
 
-    @GuardedBy("mlock")
+    @GuardedBy("mLock")
     private SkipMode mSkipMode;
 
     SkipController(@NonNull IRadioAppService.Stub service,
@@ -128,25 +128,27 @@
         return program;
     }
 
-    void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
+    void dump(IndentingPrintWriter pw) {
+        pw.println("SkipController");
+        pw.increaseIndent();
         synchronized (mLock) {
-            pw.print(prefix); pw.print("mode: "); pw.println(mSkipMode);
-            pw.print(prefix); pw.print("current index: "); pw.println(mCurrentIndex);
+            pw.printf("mode: %s\n", mSkipMode);
+            pw.printf("current index: %d\n", mCurrentIndex);
             if (mFavorites == null || mFavorites.isEmpty()) {
-                pw.print(prefix); pw.println("no favorites");
-                return;
-            }
-            int size = mFavorites.size();
-            pw.print(prefix); pw.print(size); pw.println(" favorites: ");
-            String prefix2 = prefix + "  ";
-            for (int i = 0; i < size; i++) {
-                pw.print(prefix2);
-                pw.print(i); pw.print(": "); pw.print(mFavorites.get(i).getName());
-                if (i == mCurrentIndex) {
-                    pw.print(" (current)");
+                pw.println("no favorites");
+            } else {
+                pw.printf("%d favorites:\n", mFavorites.size());
+                pw.increaseIndent();
+                for (int i = 0; i < mFavorites.size(); i++) {
+                    pw.printf("Favorite[%d]: %s ", i, mFavorites.get(i).getName());
+                    if (i == mCurrentIndex) {
+                        pw.printf(" is current");
+                    }
+                    pw.println();
                 }
-                pw.println();
+                pw.decreaseIndent();
             }
         }
+        pw.decreaseIndent();
     }
 }