luma/rico_aosp: adding the Rico AOSP patch.

This patch allows building a Marshmallow build for the Nexus 6P
devices that can dump the View Hierachy from the device in JSON
format with low latency (around 0.1s).

Change-Id: Ida64156b051233c6643f1951b9de9d3b6754c98a
diff --git a/rico_aosp/nexus6p/README.md b/rico_aosp/nexus6p/README.md
new file mode 100644
index 0000000..f70f635
--- /dev/null
+++ b/rico_aosp/nexus6p/README.md
@@ -0,0 +1,82 @@
+Contains a patch that can be applied to the platforms/base repository of the
+AOSP source code intended for the Nexus 6P device (android-6.0.1_r45).
+
+## Usage
+
+### Setting Up the Build Environment
+Instructions on the [AOSP website](https://source.android.com/source/initializing.html).
+
+### Downloading AOSP source
+```bash
+mkdir ~/android-6.0.1_r45
+cd ~/android-6.0.1_r45
+repo init -u https://android.googlesource.com/platform/manifest -b android-6.0.1_r45
+repo sync
+ ```
+
+### Patching the source files
+```bash
+cd frameworks/base
+patch -p1 < $JIRI_ROOT/experimental/projects/rico_aosp/nexus6p/patch/patch.diff
+ ```
+
+If you plan on installing Google Play services or other GmsCore apps on this
+build, you should also apply the following patch that fixes permission issues.
+```bash
+patch -p1 < vanadium/release/projects/luma_third_party/gapps_aosp_patch/patch.diff
+ ```
+
+### Downloading Nexus 6P binaries
+```bash
+cd ~/android-6.0.1_r45
+wget https://dl.google.com/dl/android/aosp/huawei-angler-3020518-2609fde4.tgz
+tar -xvzf huawei-angler-3020518-2609fde4.tgz
+./extract-huawei-angler.sh
+rm huawei-angler-3020518-2609fde4.tgz
+wget https://dl.google.com/dl/android/aosp/qcom-angler-3020518-c3c4c7af.tgz
+tar -xvzf qcom-angler-3020518-c3c4c7af.tgz
+./extract-qcom-angler.sh
+rm qcom-angler-3020518-c3c4c7af.tgz
+ ```
+
+### Building AOSP source for Angler (Nexus 6P)
+```bash
+source build/envsetup.sh
+lunch aosp_angler-eng
+make -j16   # Uses 16 threads.
+```
+
+The output will be in android-6.0.1_r45/out/target/product/angler/
+
+### Flashing a phone
+With the phone connected over USB the following:
+* Go to Settings and click on About Phone 7 times to enable developer mode.
+* Go to Settings > Developer Options and Enable USB Debugging.
+* If it is the first time you are flashing the device, you will have to
+unlock the device. Go to Settings > Developer Options and enable OEM Unlocking.
+* Then flash the build using:
+```bash
+adb reboot bootloader
+fastboot flashing unlock # Only needed for the first time to unlock the device.
+cd out/target/product/angler
+fastboot -w flashall
+```
+### Getting view hierarchies
+With the phone connected over USB the following:
+* Start the custom view server on the phone.
+```bash
+adb shell dumpsys activity start-view-server
+```
+* Use ADB to forward a port on your local machine to port 1699 on the phone.
+```bash
+adb forward tcp:<port> tcp:1699
+```
+* Connect to the local port using TCP. The example below uses a Linux utility
+called nc.
+```bash
+nc localhost <port>
+```
+* Send the String "d\n" to request a dump of the view hierarchy. Each response
+will contain the view hierarchy in JSON format followed by a line with the
+string "RICO_JSON_END".
+* Send the String "s\n" to stop the server.
diff --git a/rico_aosp/nexus6p/patch/LICENSE b/rico_aosp/nexus6p/patch/LICENSE
new file mode 100644
index 0000000..ff5d186
--- /dev/null
+++ b/rico_aosp/nexus6p/patch/LICENSE
@@ -0,0 +1,337 @@
+The following is the standard NOTICE file from the Android Open Source Project:
+
+   =========================================================================
+   ==  NOTICE file corresponding to the section 4 d of                    ==
+   ==  the Apache License, Version 2.0,                                   ==
+   ==  in this case for the Android-specific code.                        ==
+   =========================================================================
+
+Android Code
+Copyright 2005-2008 The Android Open Source Project
+
+This product includes software developed as part of
+The Android Open Source Project (http://source.android.com).
+
+   =========================================================================
+   ==  NOTICE file corresponding to the section 4 d of                    ==
+   ==  the Apache License, Version 2.0,                                   ==
+   ==  in this case for Apache Commons code.                              ==
+   =========================================================================
+
+Apache Commons
+Copyright 1999-2006 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
+   =========================================================================
+   ==  NOTICE file corresponding to the section 4 d of                    ==
+   ==  the Apache License, Version 2.0,                                   ==
+   ==  in this case for Jakarta Commons Logging.                          ==
+   =========================================================================
+
+Jakarta Commons Logging (JCL)
+Copyright 2005,2006 The Apache Software Foundation.
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
+   =========================================================================
+   ==  NOTICE file corresponding to the section 4 d of                    ==
+   ==  the Apache License, Version 2.0,                                   ==
+   ==  in this case for the Nuance code.                                  ==
+   =========================================================================
+
+These files are Copyright 2007 Nuance Communications, but released under
+the Apache2 License.
+
+   =========================================================================
+   ==  NOTICE file corresponding to the section 4 d of                    ==
+   ==  the Apache License, Version 2.0,                                   ==
+   ==  in this case for the Media Codecs code.                            ==
+   =========================================================================
+
+Media Codecs
+These files are Copyright 1998 - 2009 PacketVideo, but released under
+the Apache2 License.
+
+   =========================================================================
+   ==  NOTICE file corresponding to the section 4 d of                    ==
+   ==  the Apache License, Version 2.0,                                   ==
+   ==  in this case for the mDnsResponder code.                           ==
+   =========================================================================
+
+mDnsResponder TXTRecord
+This file is Copyright 2004 Apple Computer, Inc.  but released under
+the Apache2 License.
+
+
+   =========================================================================
+   ==  NOTICE file corresponding to the section 4 d of                    ==
+   ==  the Apache License, Version 2.0,                                   ==
+   ==  in this case for the TagSoup code.                                 ==
+   =========================================================================
+
+This file is part of TagSoup and is Copyright 2002-2008 by John Cowan.
+
+TagSoup is licensed under the Apache License,
+Version 2.0.  You may obtain a copy of this license at
+http://www.apache.org/licenses/LICENSE-2.0 .  You may also have
+additional legal rights not granted by this license.
+
+TagSoup is distributed in the hope that it will be useful, but
+unless required by applicable law or agreed to in writing, TagSoup
+is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
+OF ANY KIND, either express or implied; not even the implied warranty
+of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+   =========================================================================
+   ==  NOTICE file corresponding to the section 4 d of                    ==
+   ==  the Apache License, Version 2.0,                                   ==
+   ==  in this case for Additional Codecs code.                           ==
+   =========================================================================
+
+Additional Codecs
+These files are Copyright 2003-2010 VisualOn, but released under
+the Apache2 License.
+
+  =========================================================================
+  ==  NOTICE file corresponding to the section 4 d of                    ==
+  ==  the Apache License, Version 2.0,                                   ==
+  ==  in this case for the Audio Effects code.                           ==
+  =========================================================================
+
+Audio Effects
+These files are Copyright (C) 2004-2010 NXP Software and
+Copyright (C) 2010 The Android Open Source Project, but released under
+the Apache2 License.
+
+
+                               Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+
+
+UNICODE, INC. LICENSE AGREEMENT - DATA FILES AND SOFTWARE
+
+Unicode Data Files include all data files under the directories
+http://www.unicode.org/Public/, http://www.unicode.org/reports/,
+and http://www.unicode.org/cldr/data/ . Unicode Software includes any
+source code published in the Unicode Standard or under the directories
+http://www.unicode.org/Public/, http://www.unicode.org/reports/, and
+http://www.unicode.org/cldr/data/.
+
+NOTICE TO USER: Carefully read the following legal agreement. BY
+DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING UNICODE INC.'S DATA
+FILES ("DATA FILES"), AND/OR SOFTWARE ("SOFTWARE"), YOU UNEQUIVOCALLY
+ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE TERMS AND CONDITIONS OF
+THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT DOWNLOAD, INSTALL, COPY,
+DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE.
+
+COPYRIGHT AND PERMISSION NOTICE
+
+Copyright © 1991-2008 Unicode, Inc. All rights reserved. Distributed
+under the Terms of Use in http://www.unicode.org/copyright.html.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Unicode data files and any associated documentation (the
+"Data Files") or Unicode software and any associated documentation (the
+"Software") to deal in the Data Files or Software without restriction,
+including without limitation the rights to use, copy, modify, merge,
+publish, distribute, and/or sell copies of the Data Files or Software,
+and to permit persons to whom the Data Files or Software are furnished to
+do so, provided that (a) the above copyright notice(s) and this permission
+notice appear with all copies of the Data Files or Software, (b) both the
+above copyright notice(s) and this permission notice appear in associated
+documentation, and (c) there is clear notice in each modified Data File
+or in the Software as well as in the documentation associated with the
+Data File(s) or Software that the data or software has been modified.
+
+THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
+INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
+OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
+OR PERFORMANCE OF THE DATA FILES OR SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder
+shall not be used in advertising or otherwise to promote the sale, use
+or other dealings in these Data Files or Software without prior written
+authorization of the copyright holder.
diff --git a/rico_aosp/nexus6p/patch/README.google b/rico_aosp/nexus6p/patch/README.google
new file mode 100644
index 0000000..bf9365c
--- /dev/null
+++ b/rico_aosp/nexus6p/patch/README.google
@@ -0,0 +1,12 @@
+URL: https://android.googlesource.com/platform/frameworks/base/+archive/3df0241cc1b7b97c75be26ac5d30f20f9d7cf98e.tar.gz
+Version:  3df0241cc1b7b97c75be26ac5d30f20f9d7cf98e
+License: Apache License, Version 2.0
+License File: LICENSE
+
+Description:
+frameworks/base repository of the AOSP source code for Nexus 6P phones
+(android-6.0.1_r45)
+
+Local Modifications:
+Added a new activity service that dumps out details of the activity including
+its view hierarchy in JSON format.
diff --git a/rico_aosp/nexus6p/patch/patch.diff b/rico_aosp/nexus6p/patch/patch.diff
new file mode 100644
index 0000000..2126c95
--- /dev/null
+++ b/rico_aosp/nexus6p/patch/patch.diff
@@ -0,0 +1,1062 @@
+diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
+index ecd0050..5053c79 100644
+--- a/core/java/android/app/Activity.java
++++ b/core/java/android/app/Activity.java
+@@ -84,6 +84,7 @@ import android.view.ActionMode;
+ import android.view.ContextMenu;
+ import android.view.ContextMenu.ContextMenuInfo;
+ import android.view.ContextThemeWrapper;
++import android.view.GestureDetector;
+ import android.view.KeyEvent;
+ import android.view.LayoutInflater;
+ import android.view.Menu;
+@@ -104,6 +105,11 @@ import android.view.WindowManagerGlobal;
+ import android.view.accessibility.AccessibilityEvent;
+ import android.widget.AdapterView;
+ 
++import org.json.JSONArray;
++import org.json.JSONException;
++import org.json.JSONObject;
++import org.json.JSONStringer;
++
+ import java.io.FileDescriptor;
+ import java.io.PrintWriter;
+ import java.lang.annotation.Retention;
+@@ -700,6 +706,74 @@ public class Activity extends ContextThemeWrapper
+     }
+     private SparseArray<ManagedDialog> mManagedDialogs;
+ 
++    /* RICO: Logs gestures on activities through logcat. */
++    private GestureDetector mRicoGestureDetector;
++    private GestureLogger mRicoGestureLogger;
++
++    private static class GestureLogger implements GestureDetector.OnGestureListener,
++            GestureDetector.OnDoubleTapListener {
++        private String activityName;
++        private static final String DEBUG_TAG = "RICO_Gestures";
++
++        public GestureLogger(String activityName) {
++            this.activityName = activityName;
++        }
++
++        @Override
++        public boolean onDown(MotionEvent event) {
++            Log.d(DEBUG_TAG, "down:" + activityName);
++            return true;
++        }
++
++        @Override
++        public boolean onFling(MotionEvent event1, MotionEvent event2,
++                float velocityX, float velocityY) {
++            Log.d(DEBUG_TAG, "fling:" + activityName);
++            return true;
++        }
++
++        @Override
++        public void onLongPress(MotionEvent event) {
++            Log.d(DEBUG_TAG, "long_press:" + activityName);
++        }
++
++        @Override
++        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
++                float distanceY) {
++            Log.d(DEBUG_TAG, "scroll:" + activityName);
++            return true;
++        }
++
++        @Override
++        public void onShowPress(MotionEvent event) {
++            Log.d(DEBUG_TAG, "show_press:" + activityName);
++        }
++
++        @Override
++        public boolean onSingleTapUp(MotionEvent event) {
++            Log.d(DEBUG_TAG, "single_tap_up:" + activityName);
++            return true;
++        }
++
++        @Override
++        public boolean onDoubleTap(MotionEvent event) {
++            Log.d(DEBUG_TAG, "double_tap:" + activityName);
++            return true;
++        }
++
++        @Override
++        public boolean onDoubleTapEvent(MotionEvent event) {
++            Log.d(DEBUG_TAG, "double_tap_event:" + activityName);
++            return true;
++        }
++
++        @Override
++        public boolean onSingleTapConfirmed(MotionEvent event) {
++            Log.d(DEBUG_TAG, "single_tap_confirmed:" + activityName);
++            return true;
++        }
++    }
++
+     // set by the thread after the constructor and before onCreate(Bundle savedInstanceState) is called.
+     private Instrumentation mInstrumentation;
+     private IBinder mToken;
+@@ -924,6 +998,13 @@ public class Activity extends ContextThemeWrapper
+             mVoiceInteractor.attachActivity(this);
+         }
+         mCalled = true;
++
++        /*
++         * RICO: Initializes the gesture logger.
++         */
++        mRicoGestureLogger = new GestureLogger(this.getComponentName().flattenToString());
++        mRicoGestureDetector = new GestureDetector(this, mRicoGestureLogger);
++        mRicoGestureDetector.setOnDoubleTapListener(mRicoGestureLogger);
+     }
+ 
+     /**
+@@ -5560,6 +5641,17 @@ public class Activity extends ContextThemeWrapper
+     }
+ 
+     /**
++     * RICO: Used to access FragmentManager instance to obtain active and
++     * added fragment names. dumpActiveAndAddedFragments() is a custom method
++     * in the FragmentManager class that we added.
++     * @hide
++     */
++    public void dumpFragments(JSONObject view) throws JSONException {
++        FragmentManager fragManager = mFragments.getFragmentManager();
++        fragManager.dumpActiveAndAddedFragments(view);
++    }
++
++    /**
+      * Print the Activity's state into the given stream.  This gets invoked if
+      * you run "adb shell dumpsys activity &lt;activity_component_name&gt;".
+      *
+diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
+index fd88a05..94c4c00 100644
+--- a/core/java/android/app/ActivityThread.java
++++ b/core/java/android/app/ActivityThread.java
+@@ -110,6 +110,11 @@ import com.android.org.conscrypt.OpenSSLSocketImpl;
+ import com.android.org.conscrypt.TrustedCertificateStore;
+ import com.google.android.collect.Lists;
+ 
++import org.json.JSONArray;
++import org.json.JSONException;
++import org.json.JSONObject;
++import org.json.JSONStringer;
++
+ import java.io.File;
+ import java.io.FileDescriptor;
+ import java.io.FileOutputStream;
+@@ -942,6 +947,11 @@ public final class ActivityThread {
+             sendMessage(H.SCHEDULE_CRASH, msg);
+         }
+ 
++        /*
++         * RICO: This method sends an asynchronous message that is put onto a queue.
++         * The ActivityThread class later will handle the message with the method
++         * handleMessage().
++         */
+         public void dumpActivity(FileDescriptor fd, IBinder activitytoken,
+                 String prefix, String[] args) {
+             DumpComponentInfo data = new DumpComponentInfo();
+@@ -950,7 +960,24 @@ public final class ActivityThread {
+                 data.token = activitytoken;
+                 data.prefix = prefix;
+                 data.args = args;
+-                sendMessage(H.DUMP_ACTIVITY, data, 0, 0, true /*async*/);
++                boolean stream = false;
++
++                for (String arg: args) {
++                    if ("stream".equals(arg)) {
++                        stream = true;
++                        break;
++                    }
++                }
++                /*
++                 * If the stream argument is used when calling dumpActivity,
++                 * DUMP_ACTIVITY_TO_STREAM code will be sent. This results in the
++                 * invocation of a custom method (handleDumpActivityUIStream())
++                 */
++                if (stream) {
++                    sendMessage(H.DUMP_ACTIVITY_TO_STREAM, data, 0, 0, true /*async*/);
++                } else {
++                    sendMessage(H.DUMP_ACTIVITY, data, 0, 0, true /*async*/);
++                }
+             } catch (IOException e) {
+                 Slog.w(TAG, "dumpActivity failed", e);
+             }
+@@ -1275,6 +1302,7 @@ public final class ActivityThread {
+         public static final int CANCEL_VISIBLE_BEHIND = 147;
+         public static final int BACKGROUND_VISIBLE_BEHIND_CHANGED = 148;
+         public static final int ENTER_ANIMATION_COMPLETE = 149;
++        public static final int DUMP_ACTIVITY_TO_STREAM = 150;
+ 
+         String codeToString(int code) {
+             if (DEBUG_MESSAGES) {
+@@ -1328,6 +1356,7 @@ public final class ActivityThread {
+                     case CANCEL_VISIBLE_BEHIND: return "CANCEL_VISIBLE_BEHIND";
+                     case BACKGROUND_VISIBLE_BEHIND_CHANGED: return "BACKGROUND_VISIBLE_BEHIND_CHANGED";
+                     case ENTER_ANIMATION_COMPLETE: return "ENTER_ANIMATION_COMPLETE";
++                    case DUMP_ACTIVITY_TO_STREAM: return "DUMP_ACTIVITY_TO_STREAM";
+                 }
+             }
+             return Integer.toString(code);
+@@ -1557,6 +1586,9 @@ public final class ActivityThread {
+                 case ENTER_ANIMATION_COMPLETE:
+                     handleEnterAnimationComplete((IBinder) msg.obj);
+                     break;
++                case DUMP_ACTIVITY_TO_STREAM:
++                    handleDumpActivityToStream((DumpComponentInfo) msg.obj);
++                    break;
+             }
+             if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));
+         }
+@@ -2981,6 +3013,47 @@ public final class ActivityThread {
+         }
+     }
+ 
++    /*
++     * RICO: When the ActivityThread asynchronously receives a command to dump
++     * activity with the argument stream, this method will be called.
++     * The current top window's UI hierarchy will be dumped to the file descriptor
++     * contained in the arguments. The current root view of
++     * the top window is obtained by calling the modified getCurrentFocusRoot()
++     * method of the WindowManagerGlobal class.
++     */
++    private void handleDumpActivityToStream(DumpComponentInfo info) {
++        final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
++        try {
++            ActivityClientRecord r = mActivities.get(info.token);
++            JSONObject view = new JSONObject();
++            if (r != null && r.activity != null) {
++                r.activity.dumpFragments(view);
++
++                WindowManagerGlobal wmg = WindowManagerGlobal.getInstance();
++                view.put("is_keyboard_deployed", wmg.isKeyboardDeployed());
++
++                ViewRootImpl root = wmg.getCurrentFocusRoot();
++                if (root == null) {
++                    view.put("is_external", true);
++                } else {
++                    root.dumpRoot(view);
++                }
++
++                PrintWriter pw = new FastPrintWriter(new FileOutputStream(
++                        info.fd.getFileDescriptor()));
++                pw.print("\"activity\": ");
++                pw.print(view.toString());
++                pw.flush();
++            }
++        } catch (JSONException e) {
++            Log.e("RICO", "Exception in serializing View object to JSON.");
++            Log.e("RICO", e.getMessage());
++        }  finally {
++            IoUtils.closeQuietly(info.fd);
++            StrictMode.setThreadPolicy(oldPolicy);
++        }
++    }
++
+     private void handleDumpProvider(DumpComponentInfo info) {
+         final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+         try {
+diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
+index 696ccdb..cd17a45 100644
+--- a/core/java/android/app/FragmentManager.java
++++ b/core/java/android/app/FragmentManager.java
+@@ -45,6 +45,11 @@ import android.view.View;
+ import android.view.ViewGroup;
+ import com.android.internal.util.FastPrintWriter;
+ 
++import org.json.JSONArray;
++import org.json.JSONException;
++import org.json.JSONObject;
++import org.json.JSONStringer;
++
+ import java.io.FileDescriptor;
+ import java.io.PrintWriter;
+ import java.util.ArrayList;
+@@ -333,6 +338,13 @@ public abstract class FragmentManager {
+     public abstract boolean isDestroyed();
+ 
+     /**
++     * RICO: Print the FragmentManager's state into the given stream.
++     *
++     * @hide
++     */
++    public abstract void dumpActiveAndAddedFragments(JSONObject view) throws JSONException;
++
++    /**
+      * Print the FragmentManager's state into the given stream.
+      *
+      * @param prefix Text to print at the front of each line.
+@@ -701,6 +713,35 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate
+         return sb.toString();
+     }
+ 
++    /*
++     * RICO: Adds the names of fragments from a list to the JSONObject for the view.
++     */
++    private void dumpFragments(JSONObject view,  ArrayList<Fragment> fragmentList,
++                               String name) throws JSONException {
++        JSONArray fragments = new JSONArray();
++        if (fragmentList != null) {
++            int numFragments = fragmentList.size();
++            for (int i = 0; i < numFragments; i++) {
++                Fragment frag = fragmentList.get(i);
++                if (frag == null) {
++                    continue;
++                }
++                int fragmentId = frag.getId();
++                if (fragmentId != 0) {
++                    fragments.put(Integer.toHexString(fragmentId));
++                }
++            }
++        }
++        view.put(name, fragments);
++    }
++
++
++    public void dumpActiveAndAddedFragments(JSONObject view) throws JSONException {
++        dumpFragments(view, mActive, "active_fragments");
++        dumpFragments(view, mAdded, "added_fragments");
++    }
++
++
+     @Override
+     public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+         String innerPrefix = prefix + "    ";
+diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
+index 5994d4f..bd6127d 100644
+--- a/core/java/android/view/IWindowManager.aidl
++++ b/core/java/android/view/IWindowManager.aidl
+@@ -271,4 +271,18 @@ interface IWindowManager
+      * @return The frame statistics or null if the window does not exist.
+      */
+     WindowContentFrameStats getWindowContentFrameStats(IBinder token);
++
++    /**
++     * Gets the flag associated with a given window.
++     *
++     * @return The flag of the currently focused window or -1 if the window does not exist.
++     */
++    int getCurrentFlag();
++
++    /**
++     * Gets state of the keyboard.
++     *
++     * @return True if the keyboard is deployed, false otherwise.
++     */
++    boolean isKeyboardDeployed();
+ }
+diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
+index 720d9a8..596350d 100644
+--- a/core/java/android/view/View.java
++++ b/core/java/android/view/View.java
+@@ -16,6 +16,7 @@
+ 
+ package android.view;
+ 
++import android.app.Activity;
+ import android.animation.AnimatorInflater;
+ import android.animation.StateListAnimator;
+ import android.annotation.CallSuper;
+@@ -95,8 +96,10 @@ import android.view.animation.Transformation;
+ import android.view.inputmethod.EditorInfo;
+ import android.view.inputmethod.InputConnection;
+ import android.view.inputmethod.InputMethodManager;
++import android.widget.AdapterView;
+ import android.widget.Checkable;
+ import android.widget.FrameLayout;
++import android.widget.LinearLayout;
+ import android.widget.ScrollBarDrawable;
+ 
+ import static android.os.Build.VERSION_CODES.*;
+@@ -108,6 +111,11 @@ import com.android.internal.view.menu.MenuBuilder;
+ import com.google.android.collect.Lists;
+ import com.google.android.collect.Maps;
+ 
++import org.json.JSONArray;
++import org.json.JSONException;
++import org.json.JSONObject;
++import org.json.JSONStringer;
++
+ import java.lang.annotation.Retention;
+ import java.lang.annotation.RetentionPolicy;
+ import java.lang.ref.WeakReference;
+@@ -4554,6 +4562,115 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
+         mAttributes = trimmed;
+     }
+ 
++    /**
++     * RICO: Gets all ancestor classes of the current object.
++     * @hide
++     */
++    private ArrayList<String> getAncestors() {
++        ArrayList<String> retVal = new ArrayList<String>();
++        Class curClass = this.getClass();
++        Class superclass = curClass.getSuperclass();
++        while (superclass != null) {
++            retVal.add(superclass.getName());
++            curClass = superclass;
++            superclass = curClass.getSuperclass();
++        }
++        return retVal;
++
++    }
++
++    /**
++     * RICO: Arranges several properties of the View instance as a JSONObject.
++     * @hide
++     */
++    public JSONObject toJson() throws JSONException {
++        JSONObject obj = new JSONObject();
++        obj.put("class", getClass().getName());
++        switch (mViewFlags & VISIBILITY_MASK) {
++          case VISIBLE:
++              obj.put("visibility", "visible");
++              break;
++          case INVISIBLE:
++              obj.put("visibility", "invisible");
++              break;
++          case GONE:
++              obj.put("visibility", "gone");
++              break;
++          default:
++              break;
++        }
++        obj.put("visible-to-user", this.isVisibleToUser());
++        obj.put("adapter-view", (this instanceof AdapterView));
++        obj.put("focusable", (mViewFlags & FOCUSABLE_MASK) == FOCUSABLE);
++        obj.put("enabled", (mViewFlags & ENABLED_MASK) == ENABLED);
++        obj.put("draw", (mViewFlags & DRAW_MASK) == WILL_NOT_DRAW);
++        obj.put("scrollable-horizontal", (mViewFlags & SCROLLBARS_HORIZONTAL) != 0);
++        obj.put("scrollable-vertical", (mViewFlags & SCROLLBARS_VERTICAL) != 0);
++        obj.put("clickable", (mViewFlags & CLICKABLE) != 0 );
++        JSONArray ancestors = new JSONArray();
++        for (String className : getAncestors()) {
++            ancestors.put(className);
++        }
++        obj.put("ancestors", ancestors );
++        obj.put("pointer", Integer.toHexString(System.identityHashCode(this)));
++        obj.put("long-clickable", (mViewFlags & LONG_CLICKABLE) != 0);
++        obj.put("focused", (mPrivateFlags & PFLAG_FOCUSED) != 0);
++        obj.put("selected", (mPrivateFlags & PFLAG_SELECTED) != 0);
++        if ((mPrivateFlags & PFLAG_PREPRESSED) == 0) {
++            obj.put("pressed", (mPrivateFlags & PFLAG_PRESSED) != 0 ? "pressed" : "not_pressed");
++        } else {
++            obj.put("pressed", "prepressed");
++        }
++        JSONArray jsonRelBounds = new JSONArray();
++        jsonRelBounds.put(mLeft);
++        jsonRelBounds.put(mTop);
++        jsonRelBounds.put(mRight);
++        jsonRelBounds.put(mBottom);
++        if (mAttachInfo == null) {
++            obj.put("abs-pos", false);
++            obj.put("bounds", jsonRelBounds);
++        } else {
++            obj.put("abs-pos", true);
++            Rect bounds = mAttachInfo.mTmpInvalRect;
++            getBoundsOnScreen(bounds, true);
++            JSONArray jsonBounds = new JSONArray();
++            jsonBounds.put(bounds.left);
++            jsonBounds.put(bounds.top);
++            jsonBounds.put(bounds.right);
++            jsonBounds.put(bounds.bottom);
++            obj.put("bounds", jsonBounds);
++            obj.put("rel-bounds", jsonRelBounds);
++        }
++        final int id = getId();
++        String resourceId = null;
++        String pkgname = null;
++        if (id != NO_ID) {
++            final Resources r = mResources;
++            if (Resources.resourceHasPackage(id) && r != null) {
++                try {
++                    switch (id & 0xff000000) {
++                      case 0x01000000:
++                          pkgname = "android";
++                          break;
++                      default:
++                          pkgname = r.getResourcePackageName(id);
++                          break;
++                    }
++                    String typename = r.getResourceTypeName(id);
++                    String entryname = r.getResourceEntryName(id);
++                    resourceId = pkgname + ":" + typename + "/" + entryname;
++                } catch (Resources.NotFoundException e) {
++                    Log.e("RICO", "Resources not found for some view instance.");
++                }
++            }
++        }
++        obj.put("resource-id", resourceId);
++        obj.put("package", pkgname);
++        obj.append("content-desc", mContentDescription);
++        return obj;
++    }
++
++
+     public String toString() {
+         StringBuilder out = new StringBuilder(128);
+         out.append(getClass().getName());
+@@ -4616,12 +4733,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
+                     }
+                     String typename = r.getResourceTypeName(id);
+                     String entryname = r.getResourceEntryName(id);
+-                    out.append(" ");
+-                    out.append(pkgname);
+-                    out.append(":");
+-                    out.append(typename);
+-                    out.append("/");
+-                    out.append(entryname);
++                    out.append(String.format(" %s:%s/%s", pkgname, typename, entryname));
+                 } catch (Resources.NotFoundException e) {
+                 }
+             }
+@@ -5200,10 +5312,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
+         final boolean result;
+         final ListenerInfo li = mListenerInfo;
+         if (li != null && li.mOnClickListener != null) {
++            logEvent("click", true);
+             playSoundEffect(SoundEffectConstants.CLICK);
+             li.mOnClickListener.onClick(this);
+             result = true;
+         } else {
++            logEvent("click", false);
+             result = false;
+         }
+ 
+@@ -5240,7 +5354,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
+         boolean handled = false;
+         ListenerInfo li = mListenerInfo;
+         if (li != null && li.mOnLongClickListener != null) {
++            logEvent("long_click", true);
+             handled = li.mOnLongClickListener.onLongClick(View.this);
++        } else {
++            logEvent("long_click", false);
+         }
+         if (!handled) {
+             handled = showContextMenu();
+@@ -5290,6 +5407,80 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
+     }
+ 
+     /**
++     * RICO: Returns the current Activity.
++     */
++    private Activity getActivity() {
++        Context context = getContext();
++        while (context instanceof ContextWrapper) {
++            if (context instanceof Activity) {
++                return (Activity) context;
++            }
++            context = ((ContextWrapper) context).getBaseContext();
++        }
++        return null;
++    }
++
++    private String getDisplayId() {
++        StringBuilder out = new StringBuilder(128);
++        final int id = getId();
++        if (id != NO_ID) {
++            final Resources resources = mResources;
++            if (Resources.resourceHasPackage(id) && resources != null) {
++                try {
++                    String pkgname;
++                    switch (id & 0xff000000) {
++                      case 0x01000000:
++                          pkgname = "android";
++                          break;
++                      default:
++                          pkgname = resources.getResourcePackageName(id);
++                          break;
++                    }
++                    String typename = resources.getResourceTypeName(id);
++                    String entryname = resources.getResourceEntryName(id);
++                    out.append(pkgname);
++                    out.append(":");
++                    out.append(typename);
++                    out.append("/");
++                    out.append(entryname);
++                } catch (Resources.NotFoundException e) {
++                    return "id_not_found";
++                }
++            }
++        }
++        return out.toString();
++    }
++
++    /**
++     * RICO: Outputs information to logcat when events are handled by this view.
++     * @hide
++     */
++    protected void logEvent(String type, boolean through) {
++        String pointer = Integer.toHexString(System.identityHashCode(this));
++        String id = this.getDisplayId();
++        String classname = this.getClass().getName();
++        String content = ((mContentDescription == null) ? "" : (String) mContentDescription);
++        String position = "";
++        if (mAttachInfo == null) {
++            position = String.format("Rel[%s,%s,%s,%s]", mLeft, mTop, mRight, mBottom);
++        } else {
++            Rect bounds = mAttachInfo.mTmpInvalRect;
++            getBoundsOnScreen(bounds, true);
++            position = String.format("Abs[%s,%s,%s,%s]", bounds.left, bounds.top,
++                    bounds.right, bounds.bottom);
++        }
++        String listener = Boolean.toString(through);
++        String activityName = "not_available";
++        if (getActivity() != null && getActivity().getComponentName() != null) {
++            activityName = getActivity().getComponentName().flattenToString();
++        }
++        String stringFormat = "RicoBegin:%s:%s:%s:%s:%s:%s:%s:%s:RicoEnd";
++        String outString = String.format(stringFormat, type, pointer, id, classname, content,
++                position, listener, activityName);
++        Log.d("RICO", outString);
++    }
++
++    /**
+      * Bring up the context menu for this view.
+      *
+      * @return Whether a context menu was displayed.
+@@ -10731,7 +10922,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
+      */
+     protected void onScrollChanged(int l, int t, int oldl, int oldt) {
+         notifySubtreeAccessibilityStateChangedIfNeeded();
+-
++        logEvent("scroll", true);
+         if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+             postSendViewScrolledAccessibilityEventCallback();
+         }
+@@ -19697,8 +19888,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
+         //noinspection SimplifiableIfStatement
+         if (li != null && li.mOnDragListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
+                 && li.mOnDragListener.onDrag(this, event)) {
++            logEvent("drag", true);
+             return true;
+         }
++        logEvent("drag", false);
+         return onDragEvent(event);
+     }
+ 
+diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
+index 9569422..7290c06 100644
+--- a/core/java/android/view/ViewRootImpl.java
++++ b/core/java/android/view/ViewRootImpl.java
+@@ -82,6 +82,11 @@ import com.android.internal.policy.PhoneFallbackEventHandler;
+ import com.android.internal.view.BaseSurfaceHolder;
+ import com.android.internal.view.RootViewSurfaceTaker;
+ 
++import org.json.JSONArray;
++import org.json.JSONException;
++import org.json.JSONObject;
++import org.json.JSONStringer;
++
+ import java.io.FileDescriptor;
+ import java.io.IOException;
+ import java.io.OutputStream;
+@@ -5507,6 +5512,14 @@ public final class ViewRootImpl implements ViewParent,
+         mView.debug();
+     }
+ 
++    /**
++     * RICO: Dumps the view hierarchy information in the supplied JSONObject starting from the root
++     * view.
++     */
++    public void dumpRoot(JSONObject view) throws JSONException {
++        view.put("root", dumpViewHierarchyAsJson(mView));
++    }
++
+     public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+         String innerPrefix = prefix + "  ";
+         writer.print(prefix); writer.println("ViewRoot:");
+@@ -5537,6 +5550,29 @@ public final class ViewRootImpl implements ViewParent,
+         dumpViewHierarchy(innerPrefix, writer, mView);
+     }
+ 
++    /*
++     * RICO: Recursively traverses the view hierarchy and returns a JSONObject
++     * that contains this hierarchy and also properties of each of the views.
++     */
++    private JSONObject dumpViewHierarchyAsJson(View view) throws JSONException {
++        if (view == null) {
++            return null;
++        }
++        JSONObject viewJsonObject = view.toJson();
++        if (view instanceof ViewGroup) {
++            ViewGroup grp = (ViewGroup)view;
++            final int numChildren = grp.getChildCount();
++            if (numChildren > 0) {
++                JSONArray children = new JSONArray();
++                for (int i = 0; i < numChildren; i++) {
++                    children.put(dumpViewHierarchyAsJson(grp.getChildAt(i)));
++                }
++                viewJsonObject.put("children", children);
++            }
++        }
++        return viewJsonObject;
++    }
++
+     private void dumpViewHierarchy(String prefix, PrintWriter writer, View view) {
+         writer.print(prefix);
+         if (view == null) {
+diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
+index 606168c..a428f3d 100644
+--- a/core/java/android/view/WindowManagerGlobal.java
++++ b/core/java/android/view/WindowManagerGlobal.java
+@@ -173,6 +173,51 @@ public final class WindowManagerGlobal {
+         }
+     }
+ 
++    /** RICO
++     * @return True if the keyboard is deployed, false otherwise.
++     */
++    public boolean isKeyboardDeployed() {
++        boolean retVal = false;
++        try {
++            retVal = sWindowManagerService.isKeyboardDeployed();
++        } catch (RemoteException re) {
++            re.printStackTrace();
++        }
++        return retVal;
++    }
++
++    /**
++     * RICO: Obtains the root UI Element (of ViewRootImpl class) of the current
++     * top window. Calls the custom getCurrentFlag() method in the
++     * WindowManagerService class to first obtain the flag of the current top window,
++     * then compares flag with each view roots of all windows.
++     */
++    public ViewRootImpl getCurrentFocusRoot() {
++        ViewRootImpl curViewRoot = null;
++        synchronized (mLock) {
++            int curFlag = -1;
++            try {
++                curFlag = sWindowManagerService.getCurrentFlag();
++            } catch (RemoteException re) {
++                re.printStackTrace();
++            }
++            if (curFlag == -1) {
++                return null;
++            }
++
++            final int numRoots = mRoots.size();
++            String[] mViewRoots = new String[numRoots + 1];
++            for (int i = 0; i < numRoots; ++i) {
++                ViewRootImpl ithRoot = mRoots.get(i);
++                if (ithRoot.mWindowAttributes.flags == curFlag) {
++                    curViewRoot = ithRoot;
++                }
++            }
++
++        }
++        return curViewRoot;
++    }
++
+     public String[] getViewRootNames() {
+         synchronized (mLock) {
+             final int numRoots = mRoots.size();
+diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
+index ba916ad..4f88c6e 100644
+--- a/services/core/java/com/android/server/am/ActivityManagerService.java
++++ b/services/core/java/com/android/server/am/ActivityManagerService.java
+@@ -232,6 +232,7 @@ import android.view.WindowManager;
+ import dalvik.system.VMRuntime;
+ 
+ import java.io.BufferedInputStream;
++import java.io.BufferedReader;
+ import java.io.BufferedOutputStream;
+ import java.io.DataInputStream;
+ import java.io.DataOutputStream;
+@@ -241,10 +242,14 @@ import java.io.FileInputStream;
+ import java.io.FileNotFoundException;
+ import java.io.FileOutputStream;
+ import java.io.IOException;
++import java.io.InputStream;
+ import java.io.InputStreamReader;
+ import java.io.PrintWriter;
+ import java.io.StringWriter;
+ import java.lang.ref.WeakReference;
++import java.net.InetSocketAddress;
++import java.net.ServerSocket;
++import java.net.Socket;
+ import java.nio.charset.StandardCharsets;
+ import java.util.ArrayList;
+ import java.util.Arrays;
+@@ -418,6 +423,9 @@ public final class ActivityManagerService extends ActivityManagerNative
+     // Lower delay than APP_BOOST_MESSAGE_DELAY to disable the boost
+     static final int APP_BOOST_TIMEOUT = 2500;
+ 
++    // RICO: View server runs on this port and handles requests for dumps of the view hierarchy.
++    static final int VIEW_SERVER_PORT = 1699;
++
+     private static native int nativeMigrateToBoost();
+     private static native int nativeMigrateFromBoost();
+     private boolean mIsBoosted = false;
+@@ -456,6 +464,22 @@ public final class ActivityManagerService extends ActivityManagerNative
+         return (isFg) ? mFgBroadcastQueue : mBgBroadcastQueue;
+     }
+ 
++    /*
++     * RICO: Thread that runs the view server.
++     */
++    static Thread sDumperThread = null;
++
++    /*
++     * RICO: Stores the current top activity obtained from the modified window manager.
++     */
++    volatile ActivityRecord mCurrentTopActivity = null;
++
++    /*
++     * RICO: Initialized by the dumper thread and used to dump the JSON representation of the
++     * view hierarchy.
++     */
++    ParcelFileDescriptor pfd = null;
++
+     /**
+      * Activity we have told the window manager to have key focus.
+      */
+@@ -13109,6 +13133,9 @@ public final class ActivityManagerService extends ActivityManagerNative
+                     }
+                 }
+                 return;
++            } else if ("start-view-server".equals(cmd)) {
++                Log.d("RICO", "RicoBegin:Starting Rico View Server:RicoEnd");
++                dumpView(fd, pw, args, opti, dumpAll);
+             } else {
+                 // Dumping a single activity?
+                 if (!dumpActivity(fd, pw, cmd, args, opti, dumpAll)) {
+@@ -13991,6 +14018,109 @@ public final class ActivityManagerService extends ActivityManagerNative
+         return true;
+     }
+ 
++
++    /**
++     * RICO: This command dumps the view hierarchy of the top window of the top
++     * activity when a message is received from the client. It uses a custom
++     * dumpActivity() method in ActivityThread class to obtain the top window
++     * from an activity.
++     */
++    protected void dumpView(FileDescriptor fd, PrintWriter pw, String[] args, int opti,
++                            boolean dumpAll) {
++
++        // If sDumper Thread is running, we ignore the command to start it.
++        if (sDumperThread != null && sDumperThread.isAlive()) {
++            Log.d("RICO", "RicoBegin:Rico View Server is already running!:RicoEnd");
++            return;
++        }
++
++        final FileDescriptor threadFd = fd;
++        final String[] newArgs = new String[args.length - opti];
++        System.arraycopy(args, opti, newArgs, 0, args.length - opti);
++        final PrintWriter threadPw = pw;
++
++        sDumperThread = new Thread(new Runnable() {
++            public void run() {
++                String request;
++                String response;
++                Boolean stopServer = false;
++                try {
++                    ServerSocket server = new ServerSocket(VIEW_SERVER_PORT);
++                    while (true) {
++                        Socket socket = server.accept();
++                        BufferedReader in =
++                                new BufferedReader(new InputStreamReader(socket.getInputStream()));
++                        PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
++
++                        pfd = ParcelFileDescriptor.fromSocket(socket);
++
++                        while (true) {
++                            request = in.readLine();
++                            // If client closes abruptly, a null value is returned.
++                            if (request == null) {
++                                break;
++                            }
++
++                            String[] requestParts = request.split(" ");
++                            String command = null;
++                            String requestId = null;
++                            if (requestParts.length > 0) {
++                                command = requestParts[0];
++                            }
++                            if (requestParts.length > 1) {
++                                requestId = requestParts[1];
++                            }
++
++                            // If an "s" is received, stop the server.
++                            // If a "d" is received, dump the view.
++                            if ("s".equals(command)) {
++                                stopServer = true;
++                                break;
++                            } else if ("d".equals(command)) {
++                                if (requestId == null) {
++                                    out.print("{ \"request_id\": null");
++                                    Log.d("RICO", "RicoBegin:Request_ID: None:RicoEnd");
++                                } else {
++                                    out.print(String.format("{ \"request_id\": \"%s\"", requestId));
++                                    Log.d("RICO", String.format("RicoBegin:Request_ID: %s:RicoEnd", requestId));
++                                }
++
++                                mCurrentTopActivity = mStackSupervisor.getTopStackTopActivity();
++
++                                /**
++                                 * If an active activity is detected, the UI information from that
++                                 * activity will be dumped as JSON.
++                                 */
++                                if (mCurrentTopActivity == null) {
++                                    out.println(",\n\"activity_name\": null,");
++                                } else {
++                                    out.println(String.format(",\n\"activity_name\": \"%s\",",
++                                            mCurrentTopActivity.shortComponentName));
++                                    dumpActivityToStream(" ", threadFd, out, mCurrentTopActivity,
++                                            newArgs, false);
++                                }
++                                out.println("}");
++                                out.println("RICO_JSON_END");
++                                out.flush();
++                            }
++                        } // End of inner while loop (reads each line of input from client).
++                        Log.d("RICO", "RicoBegin:Closing Socket:RicoEnd");
++                        socket.close();
++                        if (stopServer) {
++                            break;
++                        }
++                    } // End of outer while loop (runs once for each client).
++                    Log.d("RICO", "RicoBegin:Stopping Server:RicoEnd");
++                    server.close();
++                } catch (IOException e) {
++                    e.printStackTrace();
++                }
++            }
++        });
++        sDumperThread.start();
++    }
++
++
+     /**
+      * Invokes IApplicationThread.dumpActivity() on the thread of the specified activity if
+      * there is a thread associated with the activity.
+@@ -14029,6 +14159,38 @@ public final class ActivityManagerService extends ActivityManagerNative
+         }
+     }
+ 
++    /**
++     * RICO: Calls the dumpActivity() method on the ActivityThread with an
++     * additional parameter called stream.
++     */
++    private void dumpActivityToStream(String prefix, FileDescriptor fd, PrintWriter pw,
++            final ActivityRecord r, String[] args, boolean dumpAll) {
++        String innerPrefix = prefix + "  ";
++
++        if (r.app != null && r.app.thread != null && pfd != null) {
++            // Flush anything that is already in the PrintWriter since the thread is going
++            // to write to the file descriptor directly.
++            pw.flush();
++            try {
++                TransferPipe tp = new TransferPipe();
++                FileDescriptor nfd = pfd.getFileDescriptor();
++                String[] newArgs = new String[args.length + 1];
++                newArgs[args.length] = "stream";
++                try {
++                    r.app.thread.dumpActivity(tp.getWriteFd().getFileDescriptor(),
++                            r.appToken, innerPrefix, newArgs);
++                    tp.go(nfd);
++                } finally {
++                    tp.kill();
++                }
++            } catch (IOException e) {
++                pw.println(innerPrefix + "Failure while dumping the activity: " + e);
++            } catch (RemoteException e) {
++                pw.println(innerPrefix + "Got a RemoteException while dumping the activity");
++            }
++        }
++    }
++
+     void dumpBroadcastsLocked(FileDescriptor fd, PrintWriter pw, String[] args,
+             int opti, boolean dumpAll, String dumpPackage) {
+         boolean needSep = false;
+diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
+index 6e34876..b049366 100644
+--- a/services/core/java/com/android/server/am/ActivityStack.java
++++ b/services/core/java/com/android/server/am/ActivityStack.java
+@@ -4206,6 +4206,22 @@ final class ActivityStack {
+         }
+     }
+ 
++    /**
++     * RICO: Gets the ActivityRecord of the activity that is at the top of the stack.
++     * The activity that is appended last to the list called mActivities is
++     * typically the current activity that is being used by the user.
++     */
++    ActivityRecord getCurrentTopActivity() {
++        if (mTaskHistory.size() == 0) {
++            return null;
++        }
++        TaskRecord topTask = mTaskHistory.get(mTaskHistory.size() - 1);
++        if (topTask.mActivities.size() == 0) {
++            return null;
++        }
++        return topTask.mActivities.get(topTask.mActivities.size() - 1);
++    }
++
+     boolean dumpActivitiesLocked(FileDescriptor fd, PrintWriter pw, boolean dumpAll,
+             boolean dumpClient, String dumpPackage, boolean needSep, String header) {
+         boolean printed = false;
+diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+index 17a86ca..f0883b9 100644
+--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
++++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+@@ -3554,6 +3554,24 @@ public final class ActivityStackSupervisor implements DisplayListener {
+         return false;
+     }
+ 
++
++    /*
++     * RICO: Gets the stacks of activities and calls the custom method getCurrentTopActivity()
++     * method to obtain the top activity of that stack. This is typically the activity that
++     * the user is interacting with.
++     */
++    ActivityRecord getTopStackTopActivity() {
++        if (mActivityDisplays.size() == 0) {
++            return null;
++        }
++        ActivityDisplay activityDisplay = mActivityDisplays.valueAt(0);
++        ArrayList<ActivityStack> stacks = activityDisplay.mStacks;
++        if (stacks.size() == 0) {
++            return null;
++        }
++        return stacks.get(stacks.size() - 1).getCurrentTopActivity();
++    }
++
+     boolean dumpActivitiesLocked(FileDescriptor fd, PrintWriter pw, boolean dumpAll,
+             boolean dumpClient, String dumpPackage) {
+         boolean printed = false;
+diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
+index c40947b..0d9b87a 100644
+--- a/services/core/java/com/android/server/wm/WindowManagerService.java
++++ b/services/core/java/com/android/server/wm/WindowManagerService.java
+@@ -3956,6 +3956,27 @@ public class WindowManagerService extends IWindowManager.Stub
+         return config;
+     }
+ 
++    /**
++     * Gets the flag associated with a given window.
++     *
++     * @return The flag of the currently focused window or -1 if the window does not exist.
++     */
++    public int getCurrentFlag() {
++        if (mCurrentFocus == null) {
++            return -1;
++        }
++        return mCurrentFocus.mAttrs.flags;
++    }
++
++    /**
++     * Gets state of the keyboard.
++     *
++     * @return True if the keyboard is deployed, false otherwise.
++     */
++    public boolean isKeyboardDeployed() {
++        return (mInputMethodWindow != null);
++    }
++
+     private Configuration updateOrientationFromAppTokensLocked(
+             Configuration currentConfig, IBinder freezeThisOneIfNeeded) {
+         if (!mDisplayReady) {