Sync sample prebuilts for mnc-docs

Synced to //developers/samples/android commit
7bc4d37c1eddd8b7a464a7f7fdc26b51387cf968.

Change-Id: I1486c8a4e171f53e04717640674f02a04d255dac
diff --git a/prebuilts/gradle/AppUsageStatistics/Application/src/main/java/com/example/android/appusagestatistics/AppUsageStatisticsFragment.java b/prebuilts/gradle/AppUsageStatistics/Application/src/main/java/com/example/android/appusagestatistics/AppUsageStatisticsFragment.java
index 9f54d02..019bde9 100644
--- a/prebuilts/gradle/AppUsageStatistics/Application/src/main/java/com/example/android/appusagestatistics/AppUsageStatisticsFragment.java
+++ b/prebuilts/gradle/AppUsageStatistics/Application/src/main/java/com/example/android/appusagestatistics/AppUsageStatisticsFragment.java
@@ -24,7 +24,6 @@
 import android.os.Bundle;
 import android.provider.Settings;
 import android.support.v4.app.Fragment;
-import android.support.v7.widget.LinearLayoutManager;
 import android.support.v7.widget.RecyclerView;
 import android.util.Log;
 import android.view.LayoutInflater;
@@ -91,10 +90,9 @@
     public void onViewCreated(View rootView, Bundle savedInstanceState) {
         super.onViewCreated(rootView, savedInstanceState);
 
-        mLayoutManager = new LinearLayoutManager(getActivity());
         mUsageListAdapter = new UsageListAdapter();
         mRecyclerView = (RecyclerView) rootView.findViewById(R.id.recyclerview_app_usage);
-        mRecyclerView.setLayoutManager(mLayoutManager);
+        mLayoutManager = mRecyclerView.getLayoutManager();
         mRecyclerView.scrollToPosition(0);
         mRecyclerView.setAdapter(mUsageListAdapter);
         mOpenUsageSettingButton = (Button) rootView.findViewById(R.id.button_open_usage_setting);
diff --git a/prebuilts/gradle/AppUsageStatistics/Application/src/main/res/layout/fragment_app_usage_statistics.xml b/prebuilts/gradle/AppUsageStatistics/Application/src/main/res/layout/fragment_app_usage_statistics.xml
index 1d567b7..297bf1e 100644
--- a/prebuilts/gradle/AppUsageStatistics/Application/src/main/res/layout/fragment_app_usage_statistics.xml
+++ b/prebuilts/gradle/AppUsageStatistics/Application/src/main/res/layout/fragment_app_usage_statistics.xml
@@ -16,11 +16,13 @@
 -->
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              xmlns:app="http://schemas.android.com/apk/res-auto"
               android:gravity="center_vertical"
               android:layout_width="match_parent"
               android:layout_height="match_parent"
               android:orientation="vertical"
-              android:padding="@dimen/margin_medium">
+              android:padding="@dimen/margin_medium"
+              >
 
     <Button android:id="@+id/button_open_usage_setting"
             android:layout_width="wrap_content"
@@ -50,6 +52,8 @@
             android:scrollbars="vertical"
             android:drawSelectorOnTop="true"
             android:layout_width="match_parent"
-            android:layout_height="match_parent"/>
+            android:layout_height="match_parent"
+            app:layoutManager="LinearLayoutManager"
+            />
 
 </LinearLayout>
diff --git a/prebuilts/gradle/DirectShare/.google/packaging.yaml b/prebuilts/gradle/DirectShare/.google/packaging.yaml
new file mode 100644
index 0000000..bd471cb
--- /dev/null
+++ b/prebuilts/gradle/DirectShare/.google/packaging.yaml
@@ -0,0 +1,18 @@
+
+# GOOGLE SAMPLE PACKAGING DATA
+#
+# This file is used by Google as part of our samples packaging process.
+# End users may safely ignore this file. It has no relevance to other systems.
+---
+status:       PUBLISHED
+technologies: [Android]
+categories:   [Content]
+languages:    [Java]
+solutions:    [Mobile]
+github:       android-DirectShare
+level:        INTERMEDIATE
+icon:         screenshots/icon-web.png
+apiRefs:
+    - android:android.service.chooser.ChooserTargetService
+    - android:android.service.chooser.ChooserTarget
+license: apache2
diff --git a/prebuilts/gradle/DirectShare/Application/build.gradle b/prebuilts/gradle/DirectShare/Application/build.gradle
new file mode 100644
index 0000000..4e8f709
--- /dev/null
+++ b/prebuilts/gradle/DirectShare/Application/build.gradle
@@ -0,0 +1,73 @@
+
+buildscript {
+    repositories {
+        jcenter()
+    }
+
+    dependencies {
+        classpath 'com.android.tools.build:gradle:1.2.3'
+    }
+}
+
+apply plugin: 'com.android.application'
+
+repositories {
+    jcenter()
+}
+
+dependencies {
+    compile "com.android.support:support-v4:23.0.0"
+    compile "com.android.support:support-v13:23.0.0"
+    compile "com.android.support:cardview-v7:23.0.0"
+}
+
+// The sample build uses multiple directories to
+// keep boilerplate and common code separate from
+// the main sample code.
+List<String> dirs = [
+    'main',     // main sample code; look here for the interesting stuff.
+    'common',   // components that are reused by multiple samples
+    'template'] // boilerplate code that is generated by the sample template process
+
+android {
+    compileSdkVersion 23
+    buildToolsVersion "23.0.0"
+
+    defaultConfig {
+        minSdkVersion 23
+        targetSdkVersion 23
+    }
+
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_7
+        targetCompatibility JavaVersion.VERSION_1_7
+    }
+
+    sourceSets {
+        main {
+            dirs.each { dir ->
+                java.srcDirs "src/${dir}/java"
+                res.srcDirs "src/${dir}/res"
+            }
+        }
+        androidTest.setRoot('tests')
+        androidTest.java.srcDirs = ['tests/src']
+
+    }
+
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/prebuilts/gradle/DirectShare/Application/src/main/AndroidManifest.xml b/prebuilts/gradle/DirectShare/Application/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..f760d68
--- /dev/null
+++ b/prebuilts/gradle/DirectShare/Application/src/main/AndroidManifest.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2015 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.android.directshare"
+    android:versionCode="1"
+    android:versionName="1.0">
+
+    <application
+        android:allowBackup="true"
+        android:icon="@mipmap/ic_launcher"
+        android:label="@string/app_name"
+        android:theme="@style/DirectShareTheme">
+
+        <activity
+            android:name=".MainActivity"
+            android:label="@string/app_name">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <activity
+            android:name=".SendMessageActivity"
+            android:label="@string/app_name"
+            android:theme="@style/DirectShareDialogTheme">
+            <intent-filter>
+                <action android:name="android.intent.action.SEND" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <data android:mimeType="text/plain" />
+            </intent-filter>
+            <meta-data
+                android:name="android.service.chooser.chooser_target_service"
+                android:value=".SampleChooserTargetService" />
+        </activity>
+
+        <activity
+            android:name=".SelectContactActivity"
+            android:label="@string/app_name"
+            android:theme="@style/DirectShareDialogTheme" />
+
+        <service
+            android:name=".SampleChooserTargetService"
+            android:label="@string/app_name"
+            android:permission="android.permission.BIND_CHOOSER_TARGET_SERVICE">
+            <intent-filter>
+                <action android:name="android.service.chooser.ChooserTargetService" />
+            </intent-filter>
+        </service>
+
+    </application>
+
+</manifest>
diff --git a/prebuilts/gradle/DirectShare/Application/src/main/java/com/example/android/directshare/Contact.java b/prebuilts/gradle/DirectShare/Application/src/main/java/com/example/android/directshare/Contact.java
new file mode 100644
index 0000000..4a1665e
--- /dev/null
+++ b/prebuilts/gradle/DirectShare/Application/src/main/java/com/example/android/directshare/Contact.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2015 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.android.directshare;
+
+/**
+ * Provides the list of dummy contacts. This sample implements this as constants, but real-life apps
+ * should use a database and such.
+ */
+public class Contact {
+
+    /**
+     * The list of dummy contacts.
+     */
+    public static final Contact[] CONTACTS = {
+            new Contact("Tereasa"),
+            new Contact("Chang"),
+            new Contact("Kory"),
+            new Contact("Clare"),
+            new Contact("Landon"),
+            new Contact("Kyle"),
+            new Contact("Deana"),
+            new Contact("Daria"),
+            new Contact("Melisa"),
+            new Contact("Sammie"),
+    };
+
+    /**
+     * The contact ID.
+     */
+    public static final String ID = "contact_id";
+
+    /**
+     * Representative invalid contact ID.
+     */
+    public static final int INVALID_ID = -1;
+
+    /**
+     * The name of this contact.
+     */
+    private final String mName;
+
+    /**
+     * Instantiates a new {@link Contact}.
+     *
+     * @param name The name of the contact.
+     */
+    public Contact(String name) {
+        mName = name;
+    }
+
+    /**
+     * Finds a {@link Contact} specified by a contact ID.
+     *
+     * @param id The contact ID. This needs to be a valid ID.
+     * @return A {@link Contact}
+     */
+    public static Contact byId(int id) {
+        return CONTACTS[id];
+    }
+
+    /**
+     * Gets the name of this contact.
+     *
+     * @return The name of this contact.
+     */
+    public String getName() {
+        return mName;
+    }
+
+    /**
+     * Gets the icon of this contact.
+     *
+     * @return The icon.
+     */
+    public int getIcon() {
+        return R.mipmap.logo_avatar;
+    }
+
+}
diff --git a/prebuilts/gradle/DirectShare/Application/src/main/java/com/example/android/directshare/ContactViewBinder.java b/prebuilts/gradle/DirectShare/Application/src/main/java/com/example/android/directshare/ContactViewBinder.java
new file mode 100644
index 0000000..5287b1c
--- /dev/null
+++ b/prebuilts/gradle/DirectShare/Application/src/main/java/com/example/android/directshare/ContactViewBinder.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2015 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.android.directshare;
+
+import android.widget.TextView;
+
+/**
+ * A simple utility to bind a {@link TextView} with a {@link Contact}.
+ */
+public class ContactViewBinder {
+
+    /**
+     * Binds the {@code textView} with the specified {@code contact}.
+     *
+     * @param contact  The contact.
+     * @param textView The TextView.
+     */
+    public static void bind(Contact contact, TextView textView) {
+        textView.setText(contact.getName());
+        textView.setCompoundDrawablesRelativeWithIntrinsicBounds(contact.getIcon(), 0, 0, 0);
+    }
+
+}
diff --git a/prebuilts/gradle/DirectShare/Application/src/main/java/com/example/android/directshare/MainActivity.java b/prebuilts/gradle/DirectShare/Application/src/main/java/com/example/android/directshare/MainActivity.java
new file mode 100644
index 0000000..d680186
--- /dev/null
+++ b/prebuilts/gradle/DirectShare/Application/src/main/java/com/example/android/directshare/MainActivity.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2015 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.android.directshare;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.Toolbar;
+
+/**
+ * Provides the landing screen of this sample. There is nothing particularly interesting here. All
+ * the codes related to the Direct Share feature are in {@link SampleChooserTargetService}.
+ */
+public class MainActivity extends Activity {
+
+    private EditText mEditBody;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main);
+        setActionBar((Toolbar) findViewById(R.id.toolbar));
+        mEditBody = (EditText) findViewById(R.id.body);
+        findViewById(R.id.share).setOnClickListener(mOnClickListener);
+    }
+
+    private View.OnClickListener mOnClickListener = new View.OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            switch (v.getId()) {
+                case R.id.share:
+                    share();
+                    break;
+            }
+        }
+    };
+
+    /**
+     * Emits a sample share {@link Intent}.
+     */
+    private void share() {
+        Intent sharingIntent = new Intent(android.content.Intent.ACTION_SEND);
+        sharingIntent.setType("text/plain");
+        sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, mEditBody.getText().toString());
+        startActivity(Intent.createChooser(sharingIntent, getString(R.string.send_intent_title)));
+    }
+
+}
diff --git a/prebuilts/gradle/DirectShare/Application/src/main/java/com/example/android/directshare/SampleChooserTargetService.java b/prebuilts/gradle/DirectShare/Application/src/main/java/com/example/android/directshare/SampleChooserTargetService.java
new file mode 100644
index 0000000..1e32599
--- /dev/null
+++ b/prebuilts/gradle/DirectShare/Application/src/main/java/com/example/android/directshare/SampleChooserTargetService.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2015 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.android.directshare;
+
+import android.content.ComponentName;
+import android.content.IntentFilter;
+import android.graphics.drawable.Icon;
+import android.os.Bundle;
+import android.service.chooser.ChooserTarget;
+import android.service.chooser.ChooserTargetService;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Provides the Direct Share items to the system.
+ */
+public class SampleChooserTargetService extends ChooserTargetService {
+
+    @Override
+    public List<ChooserTarget> onGetChooserTargets(ComponentName targetActivityName,
+                                                   IntentFilter matchedFilter) {
+        ComponentName componentName = new ComponentName(getPackageName(),
+                SendMessageActivity.class.getCanonicalName());
+        // The list of Direct Share items. The system will show the items the way they are sorted
+        // in this list.
+        ArrayList<ChooserTarget> targets = new ArrayList<>();
+        for (int i = 0; i < Contact.CONTACTS.length; ++i) {
+            Contact contact = Contact.byId(i);
+            Bundle extras = new Bundle();
+            extras.putInt(Contact.ID, i);
+            targets.add(new ChooserTarget(
+                    // The name of this target.
+                    contact.getName(),
+                    // The icon to represent this target.
+                    Icon.createWithResource(this, contact.getIcon()),
+                    // The ranking score for this target (0.0-1.0); the system will omit items with
+                    // low scores when there are too many Direct Share items.
+                    0.5f,
+                    // The name of the component to be launched if this target is chosen.
+                    componentName,
+                    // The extra values here will be merged into the Intent when this target is
+                    // chosen.
+                    extras));
+        }
+        return targets;
+    }
+
+}
diff --git a/prebuilts/gradle/DirectShare/Application/src/main/java/com/example/android/directshare/SelectContactActivity.java b/prebuilts/gradle/DirectShare/Application/src/main/java/com/example/android/directshare/SelectContactActivity.java
new file mode 100644
index 0000000..440facb
--- /dev/null
+++ b/prebuilts/gradle/DirectShare/Application/src/main/java/com/example/android/directshare/SelectContactActivity.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2015 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.android.directshare;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+/**
+ * The dialog for selecting a contact to share the text with. This dialog is shown when the user
+ * taps on this sample's icon rather than any of the Direct Share contacts.
+ */
+public class SelectContactActivity extends Activity {
+
+    /**
+     * The action string for Intents.
+     */
+    public static final String ACTION_SELECT_CONTACT
+            = "com.example.android.directshare.intent.action.SELECT_CONTACT";
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.select_contact);
+        Intent intent = getIntent();
+        if (!ACTION_SELECT_CONTACT.equals(intent.getAction())) {
+            finish();
+            return;
+        }
+        // Set up the list of contacts
+        ListView list = (ListView) findViewById(R.id.list);
+        list.setAdapter(mAdapter);
+        list.setOnItemClickListener(mOnItemClickListener);
+    }
+
+    private final ListAdapter mAdapter = new BaseAdapter() {
+        @Override
+        public int getCount() {
+            return Contact.CONTACTS.length;
+        }
+
+        @Override
+        public Object getItem(int i) {
+            return Contact.byId(i);
+        }
+
+        @Override
+        public long getItemId(int i) {
+            return i;
+        }
+
+        @Override
+        public View getView(int i, View view, ViewGroup parent) {
+            if (view == null) {
+                view = LayoutInflater.from(parent.getContext()).inflate(R.layout.contact, parent,
+                        false);
+            }
+            TextView textView = (TextView) view;
+            Contact contact = (Contact) getItem(i);
+            ContactViewBinder.bind(contact, textView);
+            return textView;
+        }
+    };
+
+    private final AdapterView.OnItemClickListener mOnItemClickListener
+            = new AdapterView.OnItemClickListener() {
+        @Override
+        public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
+            Intent data = new Intent();
+            data.putExtra(Contact.ID, i);
+            setResult(RESULT_OK, data);
+            finish();
+        }
+    };
+
+}
diff --git a/prebuilts/gradle/DirectShare/Application/src/main/java/com/example/android/directshare/SendMessageActivity.java b/prebuilts/gradle/DirectShare/Application/src/main/java/com/example/android/directshare/SendMessageActivity.java
new file mode 100644
index 0000000..d291172
--- /dev/null
+++ b/prebuilts/gradle/DirectShare/Application/src/main/java/com/example/android/directshare/SendMessageActivity.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2015 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.android.directshare;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.TextView;
+import android.widget.Toast;
+
+/**
+ * Provides the UI for sharing a text with a {@link Contact}.
+ */
+public class SendMessageActivity extends Activity {
+
+    /**
+     * The request code for {@link SelectContactActivity}. This is used when the user doesn't select
+     * any of Direct Share icons.
+     */
+    private static final int REQUEST_SELECT_CONTACT = 1;
+
+    /**
+     * The text to share.
+     */
+    private String mBody;
+
+    /**
+     * The ID of the contact to share the text with.
+     */
+    private int mContactId;
+
+    // View references.
+    private TextView mTextContactName;
+    private TextView mTextMessageBody;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.send_message);
+        setTitle(R.string.sending_message);
+        // View references.
+        mTextContactName = (TextView) findViewById(R.id.contact_name);
+        mTextMessageBody = (TextView) findViewById(R.id.message_body);
+        // Resolve the share Intent.
+        boolean resolved = resolveIntent(getIntent());
+        if (!resolved) {
+            finish();
+            return;
+        }
+        // Bind event handlers.
+        findViewById(R.id.send).setOnClickListener(mOnClickListener);
+        // Set up the UI.
+        prepareUi();
+        // The contact ID will not be passed on when the user clicks on the app icon rather than any
+        // of the Direct Share icons. In this case, we show another dialog for selecting a contact.
+        if (mContactId == Contact.INVALID_ID) {
+            selectContact();
+        }
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        switch (requestCode) {
+            case REQUEST_SELECT_CONTACT:
+                if (resultCode == RESULT_OK) {
+                    mContactId = data.getIntExtra(Contact.ID, Contact.INVALID_ID);
+                }
+                // Give up sharing the send_message if the user didn't choose a contact.
+                if (mContactId == Contact.INVALID_ID) {
+                    finish();
+                    return;
+                }
+                prepareUi();
+                break;
+            default:
+                super.onActivityResult(requestCode, resultCode, data);
+        }
+    }
+
+    /**
+     * Resolves the passed {@link Intent}. This method can only resolve intents for sharing a plain
+     * text. {@link #mBody} and {@link #mContactId} are modified accordingly.
+     *
+     * @param intent The {@link Intent}.
+     * @return True if the {@code intent} is resolved properly.
+     */
+    private boolean resolveIntent(Intent intent) {
+        if (Intent.ACTION_SEND.equals(intent.getAction()) &&
+                "text/plain".equals(intent.getType())) {
+            mBody = intent.getStringExtra(Intent.EXTRA_TEXT);
+            mContactId = intent.getIntExtra(Contact.ID, Contact.INVALID_ID);
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Sets up the UI.
+     */
+    private void prepareUi() {
+        if (mContactId != Contact.INVALID_ID) {
+            Contact contact = Contact.byId(mContactId);
+            ContactViewBinder.bind(contact, mTextContactName);
+        }
+        mTextMessageBody.setText(mBody);
+    }
+
+    /**
+     * Delegates selection of a {@Contact} to {@link SelectContactActivity}.
+     */
+    private void selectContact() {
+        Intent intent = new Intent(this, SelectContactActivity.class);
+        intent.setAction(SelectContactActivity.ACTION_SELECT_CONTACT);
+        startActivityForResult(intent, REQUEST_SELECT_CONTACT);
+    }
+
+    private View.OnClickListener mOnClickListener = new View.OnClickListener() {
+        @Override
+        public void onClick(View view) {
+            switch (view.getId()) {
+                case R.id.send:
+                    send();
+                    break;
+            }
+        }
+    };
+
+    /**
+     * Pretends to send the text to the contact. This only shows a dummy message.
+     */
+    private void send() {
+        Toast.makeText(this,
+                getString(R.string.message_sent, mBody, Contact.byId(mContactId).getName()),
+                Toast.LENGTH_SHORT).show();
+        finish();
+    }
+
+}
diff --git a/prebuilts/gradle/DirectShare/Application/src/main/res/drawable-hdpi/tile.9.png b/prebuilts/gradle/DirectShare/Application/src/main/res/drawable-hdpi/tile.9.png
new file mode 100644
index 0000000..1358628
--- /dev/null
+++ b/prebuilts/gradle/DirectShare/Application/src/main/res/drawable-hdpi/tile.9.png
Binary files differ
diff --git a/prebuilts/gradle/DirectShare/Application/src/main/res/layout/contact.xml b/prebuilts/gradle/DirectShare/Application/src/main/res/layout/contact.xml
new file mode 100644
index 0000000..81122e1
--- /dev/null
+++ b/prebuilts/gradle/DirectShare/Application/src/main/res/layout/contact.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2015 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"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/contact_name"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:drawablePadding="16dp"
+    android:gravity="center_vertical"
+    android:paddingBottom="4dp"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingTop="4dp"
+    android:textAppearance="@android:style/TextAppearance.Material.Body1"
+    tools:drawableStart="@mipmap/logo_avatar"
+    tools:text="Taro" />
diff --git a/prebuilts/gradle/DirectShare/Application/src/main/res/layout/main.xml b/prebuilts/gradle/DirectShare/Application/src/main/res/layout/main.xml
new file mode 100644
index 0000000..5b24b4a
--- /dev/null
+++ b/prebuilts/gradle/DirectShare/Application/src/main/res/layout/main.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Copyright 2015 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_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <Toolbar
+        android:id="@+id/toolbar"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:background="?android:attr/colorPrimary"
+        android:elevation="4dp"
+        android:minHeight="?android:attr/actionBarSize"
+        android:popupTheme="@android:style/ThemeOverlay.Material.Light"
+        android:theme="@android:style/ThemeOverlay.Material.Dark.ActionBar" />
+
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_margin="16dp"
+        android:text="@string/explanation"
+        android:textAppearance="@android:style/TextAppearance.Material.Body1" />
+
+    <View
+        android:layout_width="match_parent"
+        android:layout_height="1dp"
+        android:background="@android:color/darker_gray" />
+
+    <EditText
+        android:id="@+id/body"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginEnd="16dp"
+        android:layout_marginStart="16dp"
+        android:layout_marginTop="16dp"
+        android:hint="@string/text_to_share"
+        android:text="@string/hello" />
+
+    <Button
+        android:id="@+id/share"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="end"
+        android:layout_marginEnd="16dp"
+        android:layout_marginStart="16dp"
+        android:text="@string/share" />
+
+</LinearLayout>
diff --git a/prebuilts/gradle/DirectShare/Application/src/main/res/layout/send_message.xml b/prebuilts/gradle/DirectShare/Application/src/main/res/layout/send_message.xml
new file mode 100644
index 0000000..86671fc
--- /dev/null
+++ b/prebuilts/gradle/DirectShare/Application/src/main/res/layout/send_message.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2015 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"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginEnd="@dimen/dialog_padding"
+        android:layout_marginStart="@dimen/dialog_padding"
+        android:layout_marginTop="@dimen/dialog_padding"
+        android:orientation="horizontal">
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:gravity="center_vertical"
+            android:text="@string/to"
+            android:textAppearance="@android:style/TextAppearance.Material.Caption" />
+
+        <TextView
+            android:id="@+id/contact_name"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="16dp"
+            android:layout_weight="1"
+            android:drawablePadding="16dp"
+            android:gravity="center_vertical"
+            android:textAppearance="@android:style/TextAppearance.Material.Body1"
+            tools:drawableStart="@mipmap/logo_avatar"
+            tools:text="Taro" />
+
+    </LinearLayout>
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginEnd="@dimen/dialog_padding"
+        android:layout_marginStart="@dimen/dialog_padding"
+        android:layout_marginTop="16dp"
+        android:gravity="center_vertical"
+        android:text="@string/body"
+        android:textAppearance="@android:style/TextAppearance.Material.Caption" />
+
+    <TextView
+        android:id="@+id/message_body"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginEnd="@dimen/dialog_padding"
+        android:layout_marginStart="@dimen/dialog_padding"
+        android:gravity="top"
+        android:hint="@string/hint_body"
+        android:padding="8dp"
+        android:textAppearance="@android:style/TextAppearance.Material.Body1"
+        tools:text="Hello, world!" />
+
+    <Button
+        android:id="@+id/send"
+        style="@android:style/Widget.Material.Button.Borderless.Colored"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="end"
+        android:layout_marginBottom="@dimen/dialog_button_padding"
+        android:layout_marginEnd="@dimen/dialog_button_padding"
+        android:text="@string/send" />
+
+</LinearLayout>
diff --git a/prebuilts/gradle/DirectShare/Application/src/main/res/mipmap-hdpi/ic_launcher.png b/prebuilts/gradle/DirectShare/Application/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..988f2ec
--- /dev/null
+++ b/prebuilts/gradle/DirectShare/Application/src/main/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/prebuilts/gradle/DirectShare/Application/src/main/res/mipmap-hdpi/logo_avatar.png b/prebuilts/gradle/DirectShare/Application/src/main/res/mipmap-hdpi/logo_avatar.png
new file mode 100644
index 0000000..8892c08
--- /dev/null
+++ b/prebuilts/gradle/DirectShare/Application/src/main/res/mipmap-hdpi/logo_avatar.png
Binary files differ
diff --git a/prebuilts/gradle/DirectShare/Application/src/main/res/mipmap-mdpi/ic_launcher.png b/prebuilts/gradle/DirectShare/Application/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..0baa1cc
--- /dev/null
+++ b/prebuilts/gradle/DirectShare/Application/src/main/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/prebuilts/gradle/DirectShare/Application/src/main/res/mipmap-mdpi/logo_avatar.png b/prebuilts/gradle/DirectShare/Application/src/main/res/mipmap-mdpi/logo_avatar.png
new file mode 100644
index 0000000..c2de774
--- /dev/null
+++ b/prebuilts/gradle/DirectShare/Application/src/main/res/mipmap-mdpi/logo_avatar.png
Binary files differ
diff --git a/prebuilts/gradle/DirectShare/Application/src/main/res/mipmap-xhdpi/ic_launcher.png b/prebuilts/gradle/DirectShare/Application/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..da0aa2f
--- /dev/null
+++ b/prebuilts/gradle/DirectShare/Application/src/main/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/prebuilts/gradle/DirectShare/Application/src/main/res/mipmap-xhdpi/logo_avatar.png b/prebuilts/gradle/DirectShare/Application/src/main/res/mipmap-xhdpi/logo_avatar.png
new file mode 100644
index 0000000..10c2dc9
--- /dev/null
+++ b/prebuilts/gradle/DirectShare/Application/src/main/res/mipmap-xhdpi/logo_avatar.png
Binary files differ
diff --git a/prebuilts/gradle/DirectShare/Application/src/main/res/mipmap-xxhdpi/ic_launcher.png b/prebuilts/gradle/DirectShare/Application/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..e1cc1ff
--- /dev/null
+++ b/prebuilts/gradle/DirectShare/Application/src/main/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/prebuilts/gradle/DirectShare/Application/src/main/res/mipmap-xxhdpi/logo_avatar.png b/prebuilts/gradle/DirectShare/Application/src/main/res/mipmap-xxhdpi/logo_avatar.png
new file mode 100644
index 0000000..df02f04
--- /dev/null
+++ b/prebuilts/gradle/DirectShare/Application/src/main/res/mipmap-xxhdpi/logo_avatar.png
Binary files differ
diff --git a/prebuilts/gradle/DirectShare/Application/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/prebuilts/gradle/DirectShare/Application/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..8b0f60c
--- /dev/null
+++ b/prebuilts/gradle/DirectShare/Application/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/prebuilts/gradle/DirectShare/Application/src/main/res/mipmap-xxxhdpi/logo_avatar.png b/prebuilts/gradle/DirectShare/Application/src/main/res/mipmap-xxxhdpi/logo_avatar.png
new file mode 100644
index 0000000..dc8d376
--- /dev/null
+++ b/prebuilts/gradle/DirectShare/Application/src/main/res/mipmap-xxxhdpi/logo_avatar.png
Binary files differ
diff --git a/prebuilts/gradle/DirectShare/Application/src/main/res/values-sw600dp/template-dimens.xml b/prebuilts/gradle/DirectShare/Application/src/main/res/values-sw600dp/template-dimens.xml
new file mode 100644
index 0000000..22074a2
--- /dev/null
+++ b/prebuilts/gradle/DirectShare/Application/src/main/res/values-sw600dp/template-dimens.xml
@@ -0,0 +1,24 @@
+<!--
+  Copyright 2013 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>
+
+    <!-- Semantic definitions -->
+
+    <dimen name="horizontal_page_margin">@dimen/margin_huge</dimen>
+    <dimen name="vertical_page_margin">@dimen/margin_medium</dimen>
+
+</resources>
diff --git a/prebuilts/gradle/DirectShare/Application/src/main/res/values-sw600dp/template-styles.xml b/prebuilts/gradle/DirectShare/Application/src/main/res/values-sw600dp/template-styles.xml
new file mode 100644
index 0000000..03d1974
--- /dev/null
+++ b/prebuilts/gradle/DirectShare/Application/src/main/res/values-sw600dp/template-styles.xml
@@ -0,0 +1,25 @@
+<!--
+  Copyright 2013 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="Widget.SampleMessage">
+        <item name="android:textAppearance">?android:textAppearanceLarge</item>
+        <item name="android:lineSpacingMultiplier">1.2</item>
+        <item name="android:shadowDy">-6.5</item>
+    </style>
+
+</resources>
diff --git a/prebuilts/gradle/DirectShare/Application/src/main/res/values-v11/template-styles.xml b/prebuilts/gradle/DirectShare/Application/src/main/res/values-v11/template-styles.xml
new file mode 100644
index 0000000..8c1ea66
--- /dev/null
+++ b/prebuilts/gradle/DirectShare/Application/src/main/res/values-v11/template-styles.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright 2013 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>
+
+    <!-- Activity themes -->
+    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
+
+</resources>
diff --git a/prebuilts/gradle/DirectShare/Application/src/main/res/values-v21/base-colors.xml b/prebuilts/gradle/DirectShare/Application/src/main/res/values-v21/base-colors.xml
new file mode 100644
index 0000000..8b6ec3f
--- /dev/null
+++ b/prebuilts/gradle/DirectShare/Application/src/main/res/values-v21/base-colors.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2013 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>
+
+
+</resources>
diff --git a/prebuilts/gradle/DirectShare/Application/src/main/res/values-v21/base-template-styles.xml b/prebuilts/gradle/DirectShare/Application/src/main/res/values-v21/base-template-styles.xml
new file mode 100644
index 0000000..c778e4f
--- /dev/null
+++ b/prebuilts/gradle/DirectShare/Application/src/main/res/values-v21/base-template-styles.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2013 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>
+
+    <!-- Activity themes -->
+    <style name="Theme.Base" parent="android:Theme.Material.Light">
+    </style>
+
+</resources>
diff --git a/prebuilts/gradle/DirectShare/Application/src/main/res/values/base-strings.xml b/prebuilts/gradle/DirectShare/Application/src/main/res/values/base-strings.xml
new file mode 100644
index 0000000..b3f15fb
--- /dev/null
+++ b/prebuilts/gradle/DirectShare/Application/src/main/res/values/base-strings.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2013 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>
+    <string name="app_name">DirectShare</string>
+    <string name="intro_message">
+        <![CDATA[
+        
+            
+This sample demonstrates how to provide the Direct Share feature. The app shows some options
+directly in the list of share intent candidates.
+            
+        
+        ]]>
+    </string>
+</resources>
diff --git a/prebuilts/gradle/DirectShare/Application/src/main/res/values/colors.xml b/prebuilts/gradle/DirectShare/Application/src/main/res/values/colors.xml
new file mode 100644
index 0000000..c5a6a3d
--- /dev/null
+++ b/prebuilts/gradle/DirectShare/Application/src/main/res/values/colors.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2015 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="primary">#3F51B5</color>
+    <color name="primary_dark">#303F9F</color>
+    <color name="primary_light">#C5CAE9</color>
+    <color name="accent">#00BCD4</color>
+    <color name="primary_text">#212121</color>
+    <color name="secondary_text">#727272</color>
+    <color name="icons">#FFFFFF</color>
+    <color name="divider">#B6B6B6</color>
+</resources>
diff --git a/prebuilts/gradle/DirectShare/Application/src/main/res/values/dimens.xml b/prebuilts/gradle/DirectShare/Application/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..2d05f5d
--- /dev/null
+++ b/prebuilts/gradle/DirectShare/Application/src/main/res/values/dimens.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2015 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="dialog_padding">24dp</dimen>
+    <dimen name="dialog_button_padding">8dp</dimen>
+</resources>
diff --git a/prebuilts/gradle/DirectShare/Application/src/main/res/values/strings.xml b/prebuilts/gradle/DirectShare/Application/src/main/res/values/strings.xml
new file mode 100644
index 0000000..ddc858a
--- /dev/null
+++ b/prebuilts/gradle/DirectShare/Application/src/main/res/values/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2015 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>
+
+    <!-- MainActivity -->
+
+    <string name="explanation">
+        This app demonstrates how to implement Direct Share. Use some other app and share a text.
+        For your convenience, you can also use the input below to share the text.
+    </string>
+    <string name="hello">Hello!</string>
+    <string name="share">Share</string>
+    <string name="send_intent_title">Send a message via:</string>
+
+    <!-- SendMessageActivity -->
+
+    <string name="sending_message">Sending a message</string>
+    <string name="to">To:</string>
+    <string name="send">Send</string>
+    <string name="body">Body:</string>
+    <string name="hint_body">Edit your message.</string>
+    <string name="message_sent">Sent a message \"%1$s\" to %2$s.</string>
+    <string name="text_to_share">Text to share</string>
+
+</resources>
diff --git a/prebuilts/gradle/DirectShare/Application/src/main/res/values/styles.xml b/prebuilts/gradle/DirectShare/Application/src/main/res/values/styles.xml
new file mode 100644
index 0000000..ae312cc
--- /dev/null
+++ b/prebuilts/gradle/DirectShare/Application/src/main/res/values/styles.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2015 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="DirectShareTheme" parent="android:Theme.Material.Light.NoActionBar">
+        <item name="android:colorPrimary">@color/primary</item>
+        <item name="android:colorPrimaryDark">@color/primary_dark</item>
+        <item name="android:colorAccent">@color/accent</item>
+    </style>
+
+    <style name="DirectShareDialogTheme" parent="android:Theme.Material.Light.Dialog">
+        <item name="android:colorPrimary">@color/primary</item>
+        <item name="android:colorPrimaryDark">@color/primary_dark</item>
+        <item name="android:colorAccent">@color/accent</item>
+    </style>
+
+</resources>
diff --git a/prebuilts/gradle/DirectShare/Application/src/main/res/values/template-dimens.xml b/prebuilts/gradle/DirectShare/Application/src/main/res/values/template-dimens.xml
new file mode 100644
index 0000000..39e710b
--- /dev/null
+++ b/prebuilts/gradle/DirectShare/Application/src/main/res/values/template-dimens.xml
@@ -0,0 +1,32 @@
+<!--
+  Copyright 2013 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>
+
+    <!-- Define standard dimensions to comply with Holo-style grids and rhythm. -->
+
+    <dimen name="margin_tiny">4dp</dimen>
+    <dimen name="margin_small">8dp</dimen>
+    <dimen name="margin_medium">16dp</dimen>
+    <dimen name="margin_large">32dp</dimen>
+    <dimen name="margin_huge">64dp</dimen>
+
+    <!-- Semantic definitions -->
+
+    <dimen name="horizontal_page_margin">@dimen/margin_medium</dimen>
+    <dimen name="vertical_page_margin">@dimen/margin_medium</dimen>
+
+</resources>
diff --git a/prebuilts/gradle/DirectShare/Application/src/main/res/values/template-styles.xml b/prebuilts/gradle/DirectShare/Application/src/main/res/values/template-styles.xml
new file mode 100644
index 0000000..6e7d593
--- /dev/null
+++ b/prebuilts/gradle/DirectShare/Application/src/main/res/values/template-styles.xml
@@ -0,0 +1,42 @@
+<!--
+  Copyright 2013 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>
+
+    <!-- Activity themes -->
+
+    <style name="Theme.Base" parent="android:Theme.Light" />
+
+    <style name="Theme.Sample" parent="Theme.Base" />
+
+    <style name="AppTheme" parent="Theme.Sample" />
+    <!-- Widget styling -->
+
+    <style name="Widget" />
+
+    <style name="Widget.SampleMessage">
+        <item name="android:textAppearance">?android:textAppearanceMedium</item>
+        <item name="android:lineSpacingMultiplier">1.1</item>
+    </style>
+
+    <style name="Widget.SampleMessageTile">
+        <item name="android:background">@drawable/tile</item>
+        <item name="android:shadowColor">#7F000000</item>
+        <item name="android:shadowDy">-3.5</item>
+        <item name="android:shadowRadius">2</item>
+    </style>
+
+</resources>
diff --git a/prebuilts/gradle/DirectShare/CONTRIBUTING.md b/prebuilts/gradle/DirectShare/CONTRIBUTING.md
new file mode 100644
index 0000000..faa8b5c
--- /dev/null
+++ b/prebuilts/gradle/DirectShare/CONTRIBUTING.md
@@ -0,0 +1,35 @@
+# How to become a contributor and submit your own code
+
+## Contributor License Agreements
+
+We'd love to accept your sample apps and patches! Before we can take them, we
+have to jump a couple of legal hurdles.
+
+Please fill out either the individual or corporate Contributor License Agreement (CLA).
+
+  * If you are an individual writing original source code and you're sure you
+    own the intellectual property, then you'll need to sign an [individual CLA]
+    (https://cla.developers.google.com).
+  * If you work for a company that wants to allow you to contribute your work,
+    then you'll need to sign a [corporate CLA]
+    (https://cla.developers.google.com).
+
+Follow either of the two links above to access the appropriate CLA and
+instructions for how to sign and return it. Once we receive it, we'll be able to
+accept your pull requests.
+
+## Contributing A Patch
+
+1. Submit an issue describing your proposed change to the repo in question.
+1. The repo owner will respond to your issue promptly.
+1. If your proposed change is accepted, and you haven't already done so, sign a
+   Contributor License Agreement (see details above).
+1. Fork the desired repo, develop and test your code changes.
+1. Ensure that your code adheres to the existing style in the sample to which
+   you are contributing. Refer to the
+   [Android Code Style Guide]
+   (https://source.android.com/source/code-style.html) for the
+   recommended coding standards for this organization.
+1. Ensure that your code has an appropriate set of unit tests which all pass.
+1. Submit a pull request.
+
diff --git a/prebuilts/gradle/DirectShare/LICENSE b/prebuilts/gradle/DirectShare/LICENSE
new file mode 100644
index 0000000..4f22946
--- /dev/null
+++ b/prebuilts/gradle/DirectShare/LICENSE
@@ -0,0 +1,647 @@
+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
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "{}"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright {yyyy} {name of copyright owner}
+
+   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.
+
+All image and audio files (including *.png, *.jpg, *.svg, *.mp3, *.wav 
+and *.ogg) are licensed under the CC-BY-NC license. All other files are 
+licensed under the Apache 2 license.
+
+CC-BY-NC License
+----------------
+
+Attribution-NonCommercial-ShareAlike 4.0 International
+
+=======================================================================
+
+Creative Commons Corporation ("Creative Commons") is not a law firm and
+does not provide legal services or legal advice. Distribution of
+Creative Commons public licenses does not create a lawyer-client or
+other relationship. Creative Commons makes its licenses and related
+information available on an "as-is" basis. Creative Commons gives no
+warranties regarding its licenses, any material licensed under their
+terms and conditions, or any related information. Creative Commons
+disclaims all liability for damages resulting from their use to the
+fullest extent possible.
+
+Using Creative Commons Public Licenses
+
+Creative Commons public licenses provide a standard set of terms and
+conditions that creators and other rights holders may use to share
+original works of authorship and other material subject to copyright
+and certain other rights specified in the public license below. The
+following considerations are for informational purposes only, are not
+exhaustive, and do not form part of our licenses.
+
+     Considerations for licensors: Our public licenses are
+     intended for use by those authorized to give the public
+     permission to use material in ways otherwise restricted by
+     copyright and certain other rights. Our licenses are
+     irrevocable. Licensors should read and understand the terms
+     and conditions of the license they choose before applying it.
+     Licensors should also secure all rights necessary before
+     applying our licenses so that the public can reuse the
+     material as expected. Licensors should clearly mark any
+     material not subject to the license. This includes other CC-
+     licensed material, or material used under an exception or
+     limitation to copyright. More considerations for licensors:
+	wiki.creativecommons.org/Considerations_for_licensors
+
+     Considerations for the public: By using one of our public
+     licenses, a licensor grants the public permission to use the
+     licensed material under specified terms and conditions. If
+     the licensor's permission is not necessary for any reason--for
+     example, because of any applicable exception or limitation to
+     copyright--then that use is not regulated by the license. Our
+     licenses grant only permissions under copyright and certain
+     other rights that a licensor has authority to grant. Use of
+     the licensed material may still be restricted for other
+     reasons, including because others have copyright or other
+     rights in the material. A licensor may make special requests,
+     such as asking that all changes be marked or described.
+     Although not required by our licenses, you are encouraged to
+     respect those requests where reasonable. More_considerations
+     for the public: 
+	wiki.creativecommons.org/Considerations_for_licensees
+
+=======================================================================
+
+Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International
+Public License
+
+By exercising the Licensed Rights (defined below), You accept and agree
+to be bound by the terms and conditions of this Creative Commons
+Attribution-NonCommercial-ShareAlike 4.0 International Public License
+("Public License"). To the extent this Public License may be
+interpreted as a contract, You are granted the Licensed Rights in
+consideration of Your acceptance of these terms and conditions, and the
+Licensor grants You such rights in consideration of benefits the
+Licensor receives from making the Licensed Material available under
+these terms and conditions.
+
+
+Section 1 -- Definitions.
+
+  a. Adapted Material means material subject to Copyright and Similar
+     Rights that is derived from or based upon the Licensed Material
+     and in which the Licensed Material is translated, altered,
+     arranged, transformed, or otherwise modified in a manner requiring
+     permission under the Copyright and Similar Rights held by the
+     Licensor. For purposes of this Public License, where the Licensed
+     Material is a musical work, performance, or sound recording,
+     Adapted Material is always produced where the Licensed Material is
+     synched in timed relation with a moving image.
+
+  b. Adapter's License means the license You apply to Your Copyright
+     and Similar Rights in Your contributions to Adapted Material in
+     accordance with the terms and conditions of this Public License.
+
+  c. BY-NC-SA Compatible License means a license listed at
+     creativecommons.org/compatiblelicenses, approved by Creative
+     Commons as essentially the equivalent of this Public License.
+
+  d. Copyright and Similar Rights means copyright and/or similar rights
+     closely related to copyright including, without limitation,
+     performance, broadcast, sound recording, and Sui Generis Database
+     Rights, without regard to how the rights are labeled or
+     categorized. For purposes of this Public License, the rights
+     specified in Section 2(b)(1)-(2) are not Copyright and Similar
+     Rights.
+
+  e. Effective Technological Measures means those measures that, in the
+     absence of proper authority, may not be circumvented under laws
+     fulfilling obligations under Article 11 of the WIPO Copyright
+     Treaty adopted on December 20, 1996, and/or similar international
+     agreements.
+
+  f. Exceptions and Limitations means fair use, fair dealing, and/or
+     any other exception or limitation to Copyright and Similar Rights
+     that applies to Your use of the Licensed Material.
+
+  g. License Elements means the license attributes listed in the name
+     of a Creative Commons Public License. The License Elements of this
+     Public License are Attribution, NonCommercial, and ShareAlike.
+
+  h. Licensed Material means the artistic or literary work, database,
+     or other material to which the Licensor applied this Public
+     License.
+
+  i. Licensed Rights means the rights granted to You subject to the
+     terms and conditions of this Public License, which are limited to
+     all Copyright and Similar Rights that apply to Your use of the
+     Licensed Material and that the Licensor has authority to license.
+
+  j. Licensor means the individual(s) or entity(ies) granting rights
+     under this Public License.
+
+  k. NonCommercial means not primarily intended for or directed towards
+     commercial advantage or monetary compensation. For purposes of
+     this Public License, the exchange of the Licensed Material for
+     other material subject to Copyright and Similar Rights by digital
+     file-sharing or similar means is NonCommercial provided there is
+     no payment of monetary compensation in connection with the
+     exchange.
+
+  l. Share means to provide material to the public by any means or
+     process that requires permission under the Licensed Rights, such
+     as reproduction, public display, public performance, distribution,
+     dissemination, communication, or importation, and to make material
+     available to the public including in ways that members of the
+     public may access the material from a place and at a time
+     individually chosen by them.
+
+  m. Sui Generis Database Rights means rights other than copyright
+     resulting from Directive 96/9/EC of the European Parliament and of
+     the Council of 11 March 1996 on the legal protection of databases,
+     as amended and/or succeeded, as well as other essentially
+     equivalent rights anywhere in the world.
+
+  n. You means the individual or entity exercising the Licensed Rights
+     under this Public License. Your has a corresponding meaning.
+
+
+Section 2 -- Scope.
+
+  a. License grant.
+
+       1. Subject to the terms and conditions of this Public License,
+          the Licensor hereby grants You a worldwide, royalty-free,
+          non-sublicensable, non-exclusive, irrevocable license to
+          exercise the Licensed Rights in the Licensed Material to:
+
+            a. reproduce and Share the Licensed Material, in whole or
+               in part, for NonCommercial purposes only; and
+
+            b. produce, reproduce, and Share Adapted Material for
+               NonCommercial purposes only.
+
+       2. Exceptions and Limitations. For the avoidance of doubt, where
+          Exceptions and Limitations apply to Your use, this Public
+          License does not apply, and You do not need to comply with
+          its terms and conditions.
+
+       3. Term. The term of this Public License is specified in Section
+          6(a).
+
+       4. Media and formats; technical modifications allowed. The
+          Licensor authorizes You to exercise the Licensed Rights in
+          all media and formats whether now known or hereafter created,
+          and to make technical modifications necessary to do so. The
+          Licensor waives and/or agrees not to assert any right or
+          authority to forbid You from making technical modifications
+          necessary to exercise the Licensed Rights, including
+          technical modifications necessary to circumvent Effective
+          Technological Measures. For purposes of this Public License,
+          simply making modifications authorized by this Section 2(a)
+          (4) never produces Adapted Material.
+
+       5. Downstream recipients.
+
+            a. Offer from the Licensor -- Licensed Material. Every
+               recipient of the Licensed Material automatically
+               receives an offer from the Licensor to exercise the
+               Licensed Rights under the terms and conditions of this
+               Public License.
+
+            b. Additional offer from the Licensor -- Adapted Material.
+               Every recipient of Adapted Material from You
+               automatically receives an offer from the Licensor to
+               exercise the Licensed Rights in the Adapted Material
+               under the conditions of the Adapter's License You apply.
+
+            c. No downstream restrictions. You may not offer or impose
+               any additional or different terms or conditions on, or
+               apply any Effective Technological Measures to, the
+               Licensed Material if doing so restricts exercise of the
+               Licensed Rights by any recipient of the Licensed
+               Material.
+
+       6. No endorsement. Nothing in this Public License constitutes or
+          may be construed as permission to assert or imply that You
+          are, or that Your use of the Licensed Material is, connected
+          with, or sponsored, endorsed, or granted official status by,
+          the Licensor or others designated to receive attribution as
+          provided in Section 3(a)(1)(A)(i).
+
+  b. Other rights.
+
+       1. Moral rights, such as the right of integrity, are not
+          licensed under this Public License, nor are publicity,
+          privacy, and/or other similar personality rights; however, to
+          the extent possible, the Licensor waives and/or agrees not to
+          assert any such rights held by the Licensor to the limited
+          extent necessary to allow You to exercise the Licensed
+          Rights, but not otherwise.
+
+       2. Patent and trademark rights are not licensed under this
+          Public License.
+
+       3. To the extent possible, the Licensor waives any right to
+          collect royalties from You for the exercise of the Licensed
+          Rights, whether directly or through a collecting society
+          under any voluntary or waivable statutory or compulsory
+          licensing scheme. In all other cases the Licensor expressly
+          reserves any right to collect such royalties, including when
+          the Licensed Material is used other than for NonCommercial
+          purposes.
+
+
+Section 3 -- License Conditions.
+
+Your exercise of the Licensed Rights is expressly made subject to the
+following conditions.
+
+  a. Attribution.
+
+       1. If You Share the Licensed Material (including in modified
+          form), You must:
+
+            a. retain the following if it is supplied by the Licensor
+               with the Licensed Material:
+
+                 i. identification of the creator(s) of the Licensed
+                    Material and any others designated to receive
+                    attribution, in any reasonable manner requested by
+                    the Licensor (including by pseudonym if
+                    designated);
+
+                ii. a copyright notice;
+
+               iii. a notice that refers to this Public License;
+
+                iv. a notice that refers to the disclaimer of
+                    warranties;
+
+                 v. a URI or hyperlink to the Licensed Material to the
+                    extent reasonably practicable;
+
+            b. indicate if You modified the Licensed Material and
+               retain an indication of any previous modifications; and
+
+            c. indicate the Licensed Material is licensed under this
+               Public License, and include the text of, or the URI or
+               hyperlink to, this Public License.
+
+       2. You may satisfy the conditions in Section 3(a)(1) in any
+          reasonable manner based on the medium, means, and context in
+          which You Share the Licensed Material. For example, it may be
+          reasonable to satisfy the conditions by providing a URI or
+          hyperlink to a resource that includes the required
+          information.
+       3. If requested by the Licensor, You must remove any of the
+          information required by Section 3(a)(1)(A) to the extent
+          reasonably practicable.
+
+  b. ShareAlike.
+
+     In addition to the conditions in Section 3(a), if You Share
+     Adapted Material You produce, the following conditions also apply.
+
+       1. The Adapter's License You apply must be a Creative Commons
+          license with the same License Elements, this version or
+          later, or a BY-NC-SA Compatible License.
+
+       2. You must include the text of, or the URI or hyperlink to, the
+          Adapter's License You apply. You may satisfy this condition
+          in any reasonable manner based on the medium, means, and
+          context in which You Share Adapted Material.
+
+       3. You may not offer or impose any additional or different terms
+          or conditions on, or apply any Effective Technological
+          Measures to, Adapted Material that restrict exercise of the
+          rights granted under the Adapter's License You apply.
+
+
+Section 4 -- Sui Generis Database Rights.
+
+Where the Licensed Rights include Sui Generis Database Rights that
+apply to Your use of the Licensed Material:
+
+  a. for the avoidance of doubt, Section 2(a)(1) grants You the right
+     to extract, reuse, reproduce, and Share all or a substantial
+     portion of the contents of the database for NonCommercial purposes
+     only;
+
+  b. if You include all or a substantial portion of the database
+     contents in a database in which You have Sui Generis Database
+     Rights, then the database in which You have Sui Generis Database
+     Rights (but not its individual contents) is Adapted Material,
+     including for purposes of Section 3(b); and
+
+  c. You must comply with the conditions in Section 3(a) if You Share
+     all or a substantial portion of the contents of the database.
+
+For the avoidance of doubt, this Section 4 supplements and does not
+replace Your obligations under this Public License where the Licensed
+Rights include other Copyright and Similar Rights.
+
+
+Section 5 -- Disclaimer of Warranties and Limitation of Liability.
+
+  a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
+     EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
+     AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
+     ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
+     IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
+     WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
+     PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
+     ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
+     KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
+     ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
+
+  b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
+     TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
+     NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
+     INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
+     COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
+     USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
+     ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
+     DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
+     IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
+
+  c. The disclaimer of warranties and limitation of liability provided
+     above shall be interpreted in a manner that, to the extent
+     possible, most closely approximates an absolute disclaimer and
+     waiver of all liability.
+
+
+Section 6 -- Term and Termination.
+
+  a. This Public License applies for the term of the Copyright and
+     Similar Rights licensed here. However, if You fail to comply with
+     this Public License, then Your rights under this Public License
+     terminate automatically.
+
+  b. Where Your right to use the Licensed Material has terminated under
+     Section 6(a), it reinstates:
+
+       1. automatically as of the date the violation is cured, provided
+          it is cured within 30 days of Your discovery of the
+          violation; or
+
+       2. upon express reinstatement by the Licensor.
+
+     For the avoidance of doubt, this Section 6(b) does not affect any
+     right the Licensor may have to seek remedies for Your violations
+     of this Public License.
+
+  c. For the avoidance of doubt, the Licensor may also offer the
+     Licensed Material under separate terms or conditions or stop
+     distributing the Licensed Material at any time; however, doing so
+     will not terminate this Public License.
+
+  d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
+     License.
+
+
+Section 7 -- Other Terms and Conditions.
+
+  a. The Licensor shall not be bound by any additional or different
+     terms or conditions communicated by You unless expressly agreed.
+
+  b. Any arrangements, understandings, or agreements regarding the
+     Licensed Material not stated herein are separate from and
+     independent of the terms and conditions of this Public License.
+
+
+Section 8 -- Interpretation.
+
+  a. For the avoidance of doubt, this Public License does not, and
+     shall not be interpreted to, reduce, limit, restrict, or impose
+     conditions on any use of the Licensed Material that could lawfully
+     be made without permission under this Public License.
+
+  b. To the extent possible, if any provision of this Public License is
+     deemed unenforceable, it shall be automatically reformed to the
+     minimum extent necessary to make it enforceable. If the provision
+     cannot be reformed, it shall be severed from this Public License
+     without affecting the enforceability of the remaining terms and
+     conditions.
+
+  c. No term or condition of this Public License will be waived and no
+     failure to comply consented to unless expressly agreed to by the
+     Licensor.
+
+  d. Nothing in this Public License constitutes or may be interpreted
+     as a limitation upon, or waiver of, any privileges and immunities
+     that apply to the Licensor or You, including from the legal
+     processes of any jurisdiction or authority.
+
+=======================================================================
+
+Creative Commons is not a party to its public licenses.
+Notwithstanding, Creative Commons may elect to apply one of its public
+licenses to material it publishes and in those instances will be
+considered the "Licensor." Except for the limited purpose of indicating
+that material is shared under a Creative Commons public license or as
+otherwise permitted by the Creative Commons policies published at
+creativecommons.org/policies, Creative Commons does not authorize the
+use of the trademark "Creative Commons" or any other trademark or logo
+of Creative Commons without its prior written consent including,
+without limitation, in connection with any unauthorized modifications
+to any of its public licenses or any other arrangements,
+understandings, or agreements concerning use of licensed material. For
+the avoidance of doubt, this paragraph does not form part of the public
+licenses.
+
+Creative Commons may be contacted at creativecommons.org.
+
diff --git a/prebuilts/gradle/DirectShare/README.md b/prebuilts/gradle/DirectShare/README.md
new file mode 100644
index 0000000..4c65b2d
--- /dev/null
+++ b/prebuilts/gradle/DirectShare/README.md
@@ -0,0 +1,76 @@
+
+Android DirectShare Sample
+===================================
+
+Sample demonstrating how to show some options directly in the list of share intent candidates.
+
+Introduction
+------------
+
+[Direct Share][1] is a feature that allows apps to show their internal options directly in the
+system Intent chooser dialog. This sample is a dummy messaging app, and just like any other
+messaging apps, it receives intents for sharing a plain text. When a user shares some text from some
+other app, this sample app will be listed as an option. Using the Direct Share feature, this app
+also shows some of contacts directly in the chooser dialog.
+
+To enable Direct Share, apps need to implement a Service extending
+[ChooserTargetService][2]. Override the method [onGetChooserTargets][3] and return a list of Direct
+Share options.
+
+In your AndroidManifest.xml, add a meta-data tag in your Activity that receives the Intent. Specify
+android:name as android.service.chooser.chooser_target_service, and point the android:value to the
+Service.
+
+[1]: https://developer.android.com/reference/android/service/chooser/package-summary.html
+[2]: https://developer.android.com/reference/android/service/chooser/ChooserTargetService.html
+[3]: https://developer.android.com/reference/android/service/chooser/ChooserTargetService.html#onGetChooserTargets(android.content.ComponentName, android.content.IntentFilter)
+
+Pre-requisites
+--------------
+
+- Android SDK v23
+- Android Build Tools v23.0.0
+- Android Support Repository
+
+Screenshots
+-------------
+
+<img src="screenshots/1-main.png" height="400" alt="Screenshot"/> <img src="screenshots/2-intent.png" height="400" alt="Screenshot"/> <img src="screenshots/3-message.png" height="400" alt="Screenshot"/> 
+
+Getting Started
+---------------
+
+This sample uses the Gradle build system. To build this project, use the
+"gradlew build" command or use "Import Project" in Android Studio.
+
+Support
+-------
+
+- Google+ Community: https://plus.google.com/communities/105153134372062985968
+- Stack Overflow: http://stackoverflow.com/questions/tagged/android
+
+If you've found an error in this sample, please file an issue:
+https://github.com/googlesamples/android-DirectShare
+
+Patches are encouraged, and may be submitted by forking this project and
+submitting a pull request through GitHub. Please see CONTRIBUTING.md for more details.
+
+License
+-------
+
+Copyright 2014 The Android Open Source Project, Inc.
+
+Licensed to the Apache Software Foundation (ASF) under one or more contributor
+license agreements.  See the NOTICE file distributed with this work for
+additional information regarding copyright ownership.  The ASF licenses this
+file to you 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.
diff --git a/prebuilts/gradle/DirectShare/build.gradle b/prebuilts/gradle/DirectShare/build.gradle
new file mode 100644
index 0000000..1901ba9
--- /dev/null
+++ b/prebuilts/gradle/DirectShare/build.gradle
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/prebuilts/gradle/DirectShare/gradle/wrapper/gradle-wrapper.jar b/prebuilts/gradle/DirectShare/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..8c0fb64
--- /dev/null
+++ b/prebuilts/gradle/DirectShare/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/prebuilts/gradle/DirectShare/gradle/wrapper/gradle-wrapper.properties b/prebuilts/gradle/DirectShare/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..afb3296
--- /dev/null
+++ b/prebuilts/gradle/DirectShare/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Wed Apr 10 15:27:10 PDT 2013
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=http\://services.gradle.org/distributions/gradle-2.2.1-bin.zip
diff --git a/prebuilts/gradle/DirectShare/gradlew b/prebuilts/gradle/DirectShare/gradlew
new file mode 100755
index 0000000..91a7e26
--- /dev/null
+++ b/prebuilts/gradle/DirectShare/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+    echo "$*"
+}
+
+die ( ) {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+    [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=$((i+1))
+    done
+    case $i in
+        (0) set -- ;;
+        (1) set -- "$args0" ;;
+        (2) set -- "$args0" "$args1" ;;
+        (3) set -- "$args0" "$args1" "$args2" ;;
+        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+    JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/prebuilts/gradle/DirectShare/gradlew.bat b/prebuilts/gradle/DirectShare/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/prebuilts/gradle/DirectShare/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off

+@rem ##########################################################################

+@rem

+@rem  Gradle startup script for Windows

+@rem

+@rem ##########################################################################

+

+@rem Set local scope for the variables with windows NT shell

+if "%OS%"=="Windows_NT" setlocal

+

+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.

+set DEFAULT_JVM_OPTS=

+

+set DIRNAME=%~dp0

+if "%DIRNAME%" == "" set DIRNAME=.

+set APP_BASE_NAME=%~n0

+set APP_HOME=%DIRNAME%

+

+@rem Find java.exe

+if defined JAVA_HOME goto findJavaFromJavaHome

+

+set JAVA_EXE=java.exe

+%JAVA_EXE% -version >NUL 2>&1

+if "%ERRORLEVEL%" == "0" goto init

+

+echo.

+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.

+echo.

+echo Please set the JAVA_HOME variable in your environment to match the

+echo location of your Java installation.

+

+goto fail

+

+:findJavaFromJavaHome

+set JAVA_HOME=%JAVA_HOME:"=%

+set JAVA_EXE=%JAVA_HOME%/bin/java.exe

+

+if exist "%JAVA_EXE%" goto init

+

+echo.

+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%

+echo.

+echo Please set the JAVA_HOME variable in your environment to match the

+echo location of your Java installation.

+

+goto fail

+

+:init

+@rem Get command-line arguments, handling Windowz variants

+

+if not "%OS%" == "Windows_NT" goto win9xME_args

+if "%@eval[2+2]" == "4" goto 4NT_args

+

+:win9xME_args

+@rem Slurp the command line arguments.

+set CMD_LINE_ARGS=

+set _SKIP=2

+

+:win9xME_args_slurp

+if "x%~1" == "x" goto execute

+

+set CMD_LINE_ARGS=%*

+goto execute

+

+:4NT_args

+@rem Get arguments from the 4NT Shell from JP Software

+set CMD_LINE_ARGS=%$

+

+:execute

+@rem Setup the command line

+

+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar

+

+@rem Execute Gradle

+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%

+

+:end

+@rem End local scope for the variables with windows NT shell

+if "%ERRORLEVEL%"=="0" goto mainEnd

+

+:fail

+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of

+rem the _cmd.exe /c_ return code!

+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1

+exit /b 1

+

+:mainEnd

+if "%OS%"=="Windows_NT" endlocal

+

+:omega

diff --git a/prebuilts/gradle/DirectShare/screenshots/1-main.png b/prebuilts/gradle/DirectShare/screenshots/1-main.png
new file mode 100644
index 0000000..a0d46e4
--- /dev/null
+++ b/prebuilts/gradle/DirectShare/screenshots/1-main.png
Binary files differ
diff --git a/prebuilts/gradle/DirectShare/screenshots/2-intent.png b/prebuilts/gradle/DirectShare/screenshots/2-intent.png
new file mode 100644
index 0000000..4d6f7d0
--- /dev/null
+++ b/prebuilts/gradle/DirectShare/screenshots/2-intent.png
Binary files differ
diff --git a/prebuilts/gradle/DirectShare/screenshots/3-message.png b/prebuilts/gradle/DirectShare/screenshots/3-message.png
new file mode 100644
index 0000000..54f9e9f
--- /dev/null
+++ b/prebuilts/gradle/DirectShare/screenshots/3-message.png
Binary files differ
diff --git a/prebuilts/gradle/DirectShare/screenshots/icon-web.png b/prebuilts/gradle/DirectShare/screenshots/icon-web.png
new file mode 100644
index 0000000..ee7c557
--- /dev/null
+++ b/prebuilts/gradle/DirectShare/screenshots/icon-web.png
Binary files differ
diff --git a/prebuilts/gradle/DirectShare/settings.gradle b/prebuilts/gradle/DirectShare/settings.gradle
new file mode 100644
index 0000000..0a5c310
--- /dev/null
+++ b/prebuilts/gradle/DirectShare/settings.gradle
@@ -0,0 +1,2 @@
+
+include 'Application'
diff --git a/prebuilts/gradle/DirectorySelection/Application/src/main/java/com/example/android/directoryselection/DirectorySelectionFragment.java b/prebuilts/gradle/DirectorySelection/Application/src/main/java/com/example/android/directoryselection/DirectorySelectionFragment.java
index 4af55db..075f39b 100644
--- a/prebuilts/gradle/DirectorySelection/Application/src/main/java/com/example/android/directoryselection/DirectorySelectionFragment.java
+++ b/prebuilts/gradle/DirectorySelection/Application/src/main/java/com/example/android/directoryselection/DirectorySelectionFragment.java
@@ -27,7 +27,6 @@
 import android.provider.DocumentsContract;
 import android.provider.DocumentsContract.Document;
 import android.support.v4.app.Fragment;
-import android.support.v7.widget.LinearLayoutManager;
 import android.support.v7.widget.RecyclerView;
 import android.util.Log;
 import android.view.LayoutInflater;
@@ -124,8 +123,7 @@
             }
         });
         mRecyclerView = (RecyclerView) rootView.findViewById(R.id.recyclerview_directory_entries);
-        mLayoutManager = new LinearLayoutManager(getActivity());
-        mRecyclerView.setLayoutManager(mLayoutManager);
+        mLayoutManager = mRecyclerView.getLayoutManager();
         mRecyclerView.scrollToPosition(0);
         mAdapter = new DirectoryEntryAdapter(new ArrayList<DirectoryEntry>());
         mRecyclerView.setAdapter(mAdapter);
diff --git a/prebuilts/gradle/DirectorySelection/Application/src/main/res/layout/fragment_directory_selection.xml b/prebuilts/gradle/DirectorySelection/Application/src/main/res/layout/fragment_directory_selection.xml
index d63219c..631a8fc 100644
--- a/prebuilts/gradle/DirectorySelection/Application/src/main/res/layout/fragment_directory_selection.xml
+++ b/prebuilts/gradle/DirectorySelection/Application/src/main/res/layout/fragment_directory_selection.xml
@@ -16,6 +16,7 @@
 -->
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              xmlns:app="http://schemas.android.com/apk/res-auto"
               android:gravity="center_vertical"
               android:layout_width="match_parent"
               android:layout_height="match_parent"
@@ -68,7 +69,9 @@
             android:scrollbars="vertical"
             android:drawSelectorOnTop="true"
             android:layout_width="match_parent"
-            android:layout_height="match_parent"/>
+            android:layout_height="match_parent"
+            app:layoutManager="LinearLayoutManager"
+            />
 
 </LinearLayout>
 
diff --git a/prebuilts/gradle/MidiScope/.google/packaging.yaml b/prebuilts/gradle/MidiScope/.google/packaging.yaml
new file mode 100644
index 0000000..ad3aee6
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/.google/packaging.yaml
@@ -0,0 +1,18 @@
+
+# GOOGLE SAMPLE PACKAGING DATA
+#
+# This file is used by Google as part of our samples packaging process.
+# End users may safely ignore this file. It has no relevance to other systems.
+---
+status:       PUBLISHED
+technologies: [Android]
+categories:   [Media]
+languages:    [Java]
+solutions:    [Mobile]
+github:       android-MidiScope
+level:        INTERMEDIATE
+icon:         screenshots/icon-web.png
+apiRefs:
+    - android:android.media.midi.MidiManager
+    - android:android.media.midi.MidiReceiver
+license: apache2
diff --git a/prebuilts/gradle/MidiScope/Application/build.gradle b/prebuilts/gradle/MidiScope/Application/build.gradle
new file mode 100644
index 0000000..4e8f709
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/Application/build.gradle
@@ -0,0 +1,73 @@
+
+buildscript {
+    repositories {
+        jcenter()
+    }
+
+    dependencies {
+        classpath 'com.android.tools.build:gradle:1.2.3'
+    }
+}
+
+apply plugin: 'com.android.application'
+
+repositories {
+    jcenter()
+}
+
+dependencies {
+    compile "com.android.support:support-v4:23.0.0"
+    compile "com.android.support:support-v13:23.0.0"
+    compile "com.android.support:cardview-v7:23.0.0"
+}
+
+// The sample build uses multiple directories to
+// keep boilerplate and common code separate from
+// the main sample code.
+List<String> dirs = [
+    'main',     // main sample code; look here for the interesting stuff.
+    'common',   // components that are reused by multiple samples
+    'template'] // boilerplate code that is generated by the sample template process
+
+android {
+    compileSdkVersion 23
+    buildToolsVersion "23.0.0"
+
+    defaultConfig {
+        minSdkVersion 23
+        targetSdkVersion 23
+    }
+
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_7
+        targetCompatibility JavaVersion.VERSION_1_7
+    }
+
+    sourceSets {
+        main {
+            dirs.each { dir ->
+                java.srcDirs "src/${dir}/java"
+                res.srcDirs "src/${dir}/res"
+            }
+        }
+        androidTest.setRoot('tests')
+        androidTest.java.srcDirs = ['tests/src']
+
+    }
+
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/prebuilts/gradle/MidiScope/Application/src/main/AndroidManifest.xml b/prebuilts/gradle/MidiScope/Application/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..c2b1c43
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/Application/src/main/AndroidManifest.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2015 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
+    package="com.example.android.midiscope"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:versionCode="1"
+    android:versionName="1.0">
+
+    <uses-feature
+        android:name="android.software.midi"
+        android:required="true"/>
+
+    <application
+        android:allowBackup="true"
+        android:icon="@mipmap/ic_launcher"
+        android:label="@string/app_name"
+        android:theme="@style/MidiScopeTheme">
+
+        <activity
+            android:name=".MainActivity"
+            android:label="@string/app_name">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+
+        <service
+            android:name="MidiScope"
+            android:permission="android.permission.BIND_MIDI_DEVICE_SERVICE">
+            <intent-filter>
+                <action android:name="android.media.midi.MidiDeviceService"/>
+            </intent-filter>
+            <meta-data
+                android:name="android.media.midi.MidiDeviceService"
+                android:resource="@xml/scope_device_info"/>
+        </service>
+
+    </application>
+
+</manifest>
diff --git a/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/EventScheduler.java b/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/EventScheduler.java
new file mode 100644
index 0000000..37c0140
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/EventScheduler.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2015 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.android.common.midi;
+
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+/**
+ * Store SchedulableEvents in a timestamped buffer.
+ * Events may be written in any order.
+ * Events will be read in sorted order.
+ * Events with the same timestamp will be read in the order they were added.
+ * 
+ * Only one Thread can write into the buffer.
+ * And only one Thread can read from the buffer.
+ */
+public class EventScheduler {
+    private static final long NANOS_PER_MILLI = 1000000;
+
+    private final Object lock = new Object();
+    private SortedMap<Long, FastEventQueue> mEventBuffer;
+    // This does not have to be guarded. It is only set by the writing thread.
+    // If the reader sees a null right before being set then that is OK.
+    private FastEventQueue mEventPool = null;
+    private static final int MAX_POOL_SIZE = 200;
+
+    public EventScheduler() {
+        mEventBuffer = new TreeMap<Long, FastEventQueue>();
+    }
+
+    // If we keep at least one node in the list then it can be atomic
+    // and non-blocking.
+    private class FastEventQueue {
+        // One thread takes from the beginning of the list.
+        volatile SchedulableEvent mFirst;
+        // A second thread returns events to the end of the list.
+        volatile SchedulableEvent mLast;
+        volatile long mEventsAdded;
+        volatile long mEventsRemoved;
+
+        FastEventQueue(SchedulableEvent event) {
+            mFirst = event;
+            mLast = mFirst;
+            mEventsAdded = 1; // Always created with one event added. Never empty.
+            mEventsRemoved = 0; // None removed yet.
+        }
+
+        int size() {
+            return (int)(mEventsAdded - mEventsRemoved);
+        }
+
+        /**
+         * Do not call this unless there is more than one event
+         * in the list.
+         * @return first event in the list
+         */
+        public SchedulableEvent remove() {
+            // Take first event.
+            mEventsRemoved++;
+            SchedulableEvent event = mFirst;
+            mFirst = event.mNext;
+            return event;
+        }
+
+        /**
+         * @param event
+         */
+        public void add(SchedulableEvent event) {
+            event.mNext = null;
+            mLast.mNext = event;
+            mLast = event;
+            mEventsAdded++;
+        }
+    }
+
+    /**
+     * Base class for events that can be stored in the EventScheduler.
+     */
+    public static class SchedulableEvent {
+        private long mTimestamp;
+        private SchedulableEvent mNext = null;
+
+        /**
+         * @param timestamp
+         */
+        public SchedulableEvent(long timestamp) {
+            mTimestamp = timestamp;
+        }
+
+        /**
+         * @return timestamp
+         */
+        public long getTimestamp() {
+            return mTimestamp;
+        }
+
+        /**
+         * The timestamp should not be modified when the event is in the
+         * scheduling buffer.
+         */
+        public void setTimestamp(long timestamp) {
+            mTimestamp = timestamp;
+        }
+    }
+
+    /**
+     * Get an event from the pool.
+     * Always leave at least one event in the pool.
+     * @return event or null
+     */
+    public SchedulableEvent removeEventfromPool() {
+        SchedulableEvent event = null;
+        if (mEventPool != null && (mEventPool.size() > 1)) {
+            event = mEventPool.remove();
+        }
+        return event;
+    }
+
+    /**
+     * Return events to a pool so they can be reused.
+     *
+     * @param event
+     */
+    public void addEventToPool(SchedulableEvent event) {
+        if (mEventPool == null) {
+            mEventPool = new FastEventQueue(event);
+        // If we already have enough items in the pool then just
+        // drop the event. This prevents unbounded memory leaks.
+        } else if (mEventPool.size() < MAX_POOL_SIZE) {
+            mEventPool.add(event);
+        }
+    }
+
+    /**
+     * Add an event to the scheduler. Events with the same time will be
+     * processed in order.
+     *
+     * @param event
+     */
+    public void add(SchedulableEvent event) {
+        synchronized (lock) {
+            FastEventQueue list = mEventBuffer.get(event.getTimestamp());
+            if (list == null) {
+                long lowestTime = mEventBuffer.isEmpty() ? Long.MAX_VALUE
+                        : mEventBuffer.firstKey();
+                list = new FastEventQueue(event);
+                mEventBuffer.put(event.getTimestamp(), list);
+                // If the event we added is earlier than the previous earliest
+                // event then notify any threads waiting for the next event.
+                if (event.getTimestamp() < lowestTime) {
+                    lock.notify();
+                }
+            } else {
+                list.add(event);
+            }
+        }
+    }
+
+    // Caller must synchronize on lock before calling.
+    private SchedulableEvent removeNextEventLocked(long lowestTime) {
+        SchedulableEvent event;
+        FastEventQueue list = mEventBuffer.get(lowestTime);
+        // Remove list from tree if this is the last node.
+        if ((list.size() == 1)) {
+            mEventBuffer.remove(lowestTime);
+        }
+        event = list.remove();
+        return event;
+    }
+
+    /**
+     * Check to see if any scheduled events are ready to be processed.
+     *
+     * @param timestamp
+     * @return next event or null if none ready
+     */
+    public SchedulableEvent getNextEvent(long time) {
+        SchedulableEvent event = null;
+        synchronized (lock) {
+            if (!mEventBuffer.isEmpty()) {
+                long lowestTime = mEventBuffer.firstKey();
+                // Is it time for this list to be processed?
+                if (lowestTime <= time) {
+                    event = removeNextEventLocked(lowestTime);
+                }
+            }
+        }
+        // Log.i(TAG, "getNextEvent: event = " + event);
+        return event;
+    }
+
+    /**
+     * Return the next available event or wait until there is an event ready to
+     * be processed. This method assumes that the timestamps are in nanoseconds
+     * and that the current time is System.nanoTime().
+     *
+     * @return event
+     * @throws InterruptedException
+     */
+    public SchedulableEvent waitNextEvent() throws InterruptedException {
+        SchedulableEvent event = null;
+        while (true) {
+            long millisToWait = Integer.MAX_VALUE;
+            synchronized (lock) {
+                if (!mEventBuffer.isEmpty()) {
+                    long now = System.nanoTime();
+                    long lowestTime = mEventBuffer.firstKey();
+                    // Is it time for the earliest list to be processed?
+                    if (lowestTime <= now) {
+                        event = removeNextEventLocked(lowestTime);
+                        break;
+                    } else {
+                        // Figure out how long to sleep until next event.
+                        long nanosToWait = lowestTime - now;
+                        // Add 1 millisecond so we don't wake up before it is
+                        // ready.
+                        millisToWait = 1 + (nanosToWait / NANOS_PER_MILLI);
+                        // Clip 64-bit value to 32-bit max.
+                        if (millisToWait > Integer.MAX_VALUE) {
+                            millisToWait = Integer.MAX_VALUE;
+                        }
+                    }
+                }
+                lock.wait((int) millisToWait);
+            }
+        }
+        return event;
+    }
+}
diff --git a/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/MidiConstants.java b/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/MidiConstants.java
new file mode 100644
index 0000000..38c25d5
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/MidiConstants.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2015 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.android.common.midi;
+
+/**
+ * MIDI related constants and static methods.
+ * These values are defined in the MIDI Standard 1.0
+ * available from the MIDI Manufacturers Association.
+ */
+public class MidiConstants {
+    protected final static String TAG = "MidiTools";
+    public static final byte STATUS_COMMAND_MASK = (byte) 0xF0;
+    public static final byte STATUS_CHANNEL_MASK = (byte) 0x0F;
+
+    // Channel voice messages.
+    public static final byte STATUS_NOTE_OFF = (byte) 0x80;
+    public static final byte STATUS_NOTE_ON = (byte) 0x90;
+    public static final byte STATUS_POLYPHONIC_AFTERTOUCH = (byte) 0xA0;
+    public static final byte STATUS_CONTROL_CHANGE = (byte) 0xB0;
+    public static final byte STATUS_PROGRAM_CHANGE = (byte) 0xC0;
+    public static final byte STATUS_CHANNEL_PRESSURE = (byte) 0xD0;
+    public static final byte STATUS_PITCH_BEND = (byte) 0xE0;
+
+    // System Common Messages.
+    public static final byte STATUS_SYSTEM_EXCLUSIVE = (byte) 0xF0;
+    public static final byte STATUS_MIDI_TIME_CODE = (byte) 0xF1;
+    public static final byte STATUS_SONG_POSITION = (byte) 0xF2;
+    public static final byte STATUS_SONG_SELECT = (byte) 0xF3;
+    public static final byte STATUS_TUNE_REQUEST = (byte) 0xF6;
+    public static final byte STATUS_END_SYSEX = (byte) 0xF7;
+
+    // System Real-Time Messages
+    public static final byte STATUS_TIMING_CLOCK = (byte) 0xF8;
+    public static final byte STATUS_START = (byte) 0xFA;
+    public static final byte STATUS_CONTINUE = (byte) 0xFB;
+    public static final byte STATUS_STOP = (byte) 0xFC;
+    public static final byte STATUS_ACTIVE_SENSING = (byte) 0xFE;
+    public static final byte STATUS_RESET = (byte) 0xFF;
+
+    /** Number of bytes in a message nc from 8c to Ec */
+    public final static int CHANNEL_BYTE_LENGTHS[] = { 3, 3, 3, 3, 2, 2, 3 };
+
+    /** Number of bytes in a message Fn from F0 to FF */
+    public final static int SYSTEM_BYTE_LENGTHS[] = { 1, 2, 3, 2, 1, 1, 1, 1, 1,
+            1, 1, 1, 1, 1, 1, 1 };
+
+    /**
+     * MIDI messages, except for SysEx, are 1,2 or 3 bytes long.
+     * You can tell how long a MIDI message is from the first status byte.
+     * Do not call this for SysEx, which has variable length.
+     * @param statusByte
+     * @return number of bytes in a complete message, zero if data byte passed
+     */
+    public static int getBytesPerMessage(byte statusByte) {
+        // Java bytes are signed so we need to mask off the high bits
+        // to get a value between 0 and 255.
+        int statusInt = statusByte & 0xFF;
+        if (statusInt >= 0xF0) {
+            // System messages use low nibble for size.
+            return SYSTEM_BYTE_LENGTHS[statusInt & 0x0F];
+        } else if(statusInt >= 0x80) {
+            // Channel voice messages use high nibble for size.
+            return CHANNEL_BYTE_LENGTHS[(statusInt >> 4) - 8];
+        } else {
+            return 0; // data byte
+        }
+    }
+
+    /**
+     * @param msg
+     * @param offset
+     * @param count
+     * @return true if the entire message is ActiveSensing commands
+     */
+    public static boolean isAllActiveSensing(byte[] msg, int offset,
+            int count) {
+        // Count bytes that are not active sensing.
+        int goodBytes = 0;
+        for (int i = 0; i < count; i++) {
+            byte b = msg[offset + i];
+            if (b != MidiConstants.STATUS_ACTIVE_SENSING) {
+                goodBytes++;
+            }
+        }
+        return (goodBytes == 0);
+    }
+
+}
diff --git a/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/MidiDispatcher.java b/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/MidiDispatcher.java
new file mode 100644
index 0000000..b7f1fe1
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/MidiDispatcher.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2015 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.android.common.midi;
+
+import android.media.midi.MidiReceiver;
+import android.media.midi.MidiSender;
+
+import java.io.IOException;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * Utility class for dispatching MIDI data to a list of {@link MidiReceiver}s.
+ * This class subclasses {@link MidiReceiver} and dispatches any data it receives
+ * to its receiver list. Any receivers that throw an exception upon receiving data will
+ * be automatically removed from the receiver list, but no IOException will be returned
+ * from the dispatcher's {@link MidiReceiver#onReceive} in that case.
+ */
+public final class MidiDispatcher extends MidiReceiver {
+
+    private final CopyOnWriteArrayList<MidiReceiver> mReceivers
+            = new CopyOnWriteArrayList<MidiReceiver>();
+
+    private final MidiSender mSender = new MidiSender() {
+        /**
+         * Called to connect a {@link MidiReceiver} to the sender
+         *
+         * @param receiver the receiver to connect
+         */
+        @Override
+        public void onConnect(MidiReceiver receiver) {
+            mReceivers.add(receiver);
+        }
+
+        /**
+         * Called to disconnect a {@link MidiReceiver} from the sender
+         *
+         * @param receiver the receiver to disconnect
+         */
+        @Override
+        public void onDisconnect(MidiReceiver receiver) {
+            mReceivers.remove(receiver);
+        }
+    };
+
+    /**
+     * Returns the number of {@link MidiReceiver}s this dispatcher contains.
+     * @return the number of receivers
+     */
+    public int getReceiverCount() {
+        return mReceivers.size();
+    }
+
+    /**
+     * Returns a {@link MidiSender} which is used to add and remove
+     * {@link MidiReceiver}s
+     * to the dispatcher's receiver list.
+     * @return the dispatcher's MidiSender
+     */
+    public MidiSender getSender() {
+        return mSender;
+    }
+
+    @Override
+    public void onSend(byte[] msg, int offset, int count, long timestamp) throws IOException {
+       for (MidiReceiver receiver : mReceivers) {
+            try {
+                receiver.send(msg, offset, count, timestamp);
+            } catch (IOException e) {
+                // if the receiver fails we remove the receiver but do not propagate the exception
+                mReceivers.remove(receiver);
+            }
+        }
+    }
+
+    @Override
+    public void flush() throws IOException {
+       for (MidiReceiver receiver : mReceivers) {
+            receiver.flush();
+       }
+    }
+}
diff --git a/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/MidiEventScheduler.java b/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/MidiEventScheduler.java
new file mode 100644
index 0000000..513d393
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/MidiEventScheduler.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2015 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.android.common.midi;
+
+import android.media.midi.MidiReceiver;
+
+import java.io.IOException;
+
+/**
+ * Add MIDI Events to an EventScheduler
+ */
+public class MidiEventScheduler extends EventScheduler {
+    private static final String TAG = "MidiEventScheduler";
+    // Maintain a pool of scheduled events to reduce memory allocation.
+    // This pool increases performance by about 14%.
+    private final static int POOL_EVENT_SIZE = 16;
+    private MidiReceiver mReceiver = new SchedulingReceiver();
+
+    private class SchedulingReceiver extends MidiReceiver
+    {
+        /**
+         * Store these bytes in the EventScheduler to be delivered at the specified
+         * time.
+         */
+        @Override
+        public void onSend(byte[] msg, int offset, int count, long timestamp)
+                throws IOException {
+            MidiEvent event = createScheduledEvent(msg, offset, count, timestamp);
+            if (event != null) {
+                add(event);
+            }
+        }
+    }
+
+    public static class MidiEvent extends SchedulableEvent {
+        public int count = 0;
+        public byte[] data;
+
+        private MidiEvent(int count) {
+            super(0);
+            data = new byte[count];
+        }
+
+        private MidiEvent(byte[] msg, int offset, int count, long timestamp) {
+            super(timestamp);
+            data = new byte[count];
+            System.arraycopy(msg, offset, data, 0, count);
+            this.count = count;
+        }
+
+        @Override
+        public String toString() {
+            String text = "Event: ";
+            for (int i = 0; i < count; i++) {
+                text += data[i] + ", ";
+            }
+            return text;
+        }
+    }
+
+    /**
+     * Create an event that contains the message.
+     */
+    private MidiEvent createScheduledEvent(byte[] msg, int offset, int count,
+            long timestamp) {
+        MidiEvent event;
+        if (count > POOL_EVENT_SIZE) {
+            event = new MidiEvent(msg, offset, count, timestamp);
+        } else {
+            event = (MidiEvent) removeEventfromPool();
+            if (event == null) {
+                event = new MidiEvent(POOL_EVENT_SIZE);
+            }
+            System.arraycopy(msg, offset, event.data, 0, count);
+            event.count = count;
+            event.setTimestamp(timestamp);
+        }
+        return event;
+    }
+
+    /**
+     * Return events to a pool so they can be reused.
+     *
+     * @param event
+     */
+    @Override
+    public void addEventToPool(SchedulableEvent event) {
+        // Make sure the event is suitable for the pool.
+        if (event instanceof MidiEvent) {
+            MidiEvent midiEvent = (MidiEvent) event;
+            if (midiEvent.data.length == POOL_EVENT_SIZE) {
+                super.addEventToPool(event);
+            }
+        }
+    }
+
+    /**
+     * This MidiReceiver will write date to the scheduling buffer.
+     * @return the MidiReceiver
+     */
+    public MidiReceiver getReceiver() {
+        return mReceiver;
+    }
+
+}
diff --git a/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/MidiEventThread.java b/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/MidiEventThread.java
new file mode 100644
index 0000000..626e83c
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/MidiEventThread.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2015 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.android.common.midi;
+
+import android.media.midi.MidiSender;
+import android.util.Log;
+
+import java.io.IOException;
+
+public class MidiEventThread extends MidiEventScheduler {
+    protected static final String TAG = "MidiEventThread";
+
+    private EventThread mEventThread;
+    MidiDispatcher mDispatcher = new MidiDispatcher();
+
+    class EventThread extends Thread {
+        private boolean go = true;
+
+        @Override
+        public void run() {
+            while (go) {
+                try {
+                    MidiEvent event = (MidiEvent) waitNextEvent();
+                    try {
+                        Log.i(TAG, "Fire event " + event.data[0] + " at "
+                                + event.getTimestamp());
+                        mDispatcher.send(event.data, 0,
+                                event.count, event.getTimestamp());
+                    } catch (IOException e) {
+                        e.printStackTrace();
+                    }
+                    // Put event back in the pool for future use.
+                    addEventToPool(event);
+                } catch (InterruptedException e) {
+                    // OK, this is how we stop the thread.
+                }
+            }
+        }
+
+        /**
+         * Asynchronously tell the thread to stop.
+         */
+        public void requestStop() {
+            go = false;
+            interrupt();
+        }
+    }
+
+    public void start() {
+        stop();
+        mEventThread = new EventThread();
+        mEventThread.start();
+    }
+
+    /**
+     * Asks the thread to stop then waits for it to stop.
+     */
+    public void stop() {
+        if (mEventThread != null) {
+            mEventThread.requestStop();
+            try {
+                mEventThread.join(500);
+            } catch (InterruptedException e) {
+                Log.e(TAG,
+                        "Interrupted while waiting for MIDI EventScheduler thread to stop.");
+            } finally {
+                mEventThread = null;
+            }
+        }
+    }
+
+    public MidiSender getSender() {
+        return mDispatcher.getSender();
+    }
+
+}
diff --git a/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/MidiFramer.java b/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/MidiFramer.java
new file mode 100644
index 0000000..c274925
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/MidiFramer.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2015 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.android.common.midi;
+
+import android.media.midi.MidiReceiver;
+import android.util.Log;
+
+import java.io.IOException;
+
+/**
+ * Convert stream of arbitrary MIDI bytes into discrete messages.
+ *
+ * Parses the incoming bytes and then posts individual messages to the receiver
+ * specified in the constructor. Short messages of 1-3 bytes will be complete.
+ * System Exclusive messages may be posted in pieces.
+ *
+ * Resolves Running Status and interleaved System Real-Time messages.
+ */
+public class MidiFramer extends MidiReceiver {
+    private MidiReceiver mReceiver;
+    private byte[] mBuffer = new byte[3];
+    private int mCount;
+    private byte mRunningStatus;
+    private int mNeeded;
+    private boolean mInSysEx;
+
+    public MidiFramer(MidiReceiver receiver) {
+        mReceiver = receiver;
+    }
+
+    /*
+     * @see android.midi.MidiReceiver#onSend(byte[], int, int, long)
+     */
+    @Override
+    public void onSend(byte[] data, int offset, int count, long timestamp)
+            throws IOException {
+        int sysExStartOffset = (mInSysEx ? offset : -1);
+
+        for (int i = 0; i < count; i++) {
+            final byte currentByte = data[offset];
+            final int currentInt = currentByte & 0xFF;
+            if (currentInt >= 0x80) { // status byte?
+                if (currentInt < 0xF0) { // channel message?
+                    mRunningStatus = currentByte;
+                    mCount = 1;
+                    mNeeded = MidiConstants.getBytesPerMessage(currentByte) - 1;
+                } else if (currentInt < 0xF8) { // system common?
+                    if (currentInt == 0xF0 /* SysEx Start */) {
+                        // Log.i(TAG, "SysEx Start");
+                        mInSysEx = true;
+                        sysExStartOffset = offset;
+                    } else if (currentInt == 0xF7 /* SysEx End */) {
+                        // Log.i(TAG, "SysEx End");
+                        if (mInSysEx) {
+                            mReceiver.send(data, sysExStartOffset,
+                                offset - sysExStartOffset + 1, timestamp);
+                            mInSysEx = false;
+                            sysExStartOffset = -1;
+                        }
+                    } else {
+                        mBuffer[0] = currentByte;
+                        mRunningStatus = 0;
+                        mCount = 1;
+                        mNeeded = MidiConstants.getBytesPerMessage(currentByte) - 1;
+                    }
+                } else { // real-time?
+                    // Single byte message interleaved with other data.
+                    if (mInSysEx) {
+                        mReceiver.send(data, sysExStartOffset,
+                                offset - sysExStartOffset, timestamp);
+                        sysExStartOffset = offset + 1;
+                    }
+                    mReceiver.send(data, offset, 1, timestamp);
+                }
+            } else { // data byte
+                if (!mInSysEx) {
+                    mBuffer[mCount++] = currentByte;
+                    if (--mNeeded == 0) {
+                        if (mRunningStatus != 0) {
+                            mBuffer[0] = mRunningStatus;
+                        }
+                        mReceiver.send(mBuffer, 0, mCount, timestamp);
+                        mNeeded = MidiConstants.getBytesPerMessage(mBuffer[0]) - 1;
+                        mCount = 1;
+                    }
+                }
+            }
+            ++offset;
+        }
+
+        // send any accumulatedSysEx data
+        if (sysExStartOffset >= 0 && sysExStartOffset < offset) {
+            mReceiver.send(data, sysExStartOffset,
+                    offset - sysExStartOffset, timestamp);
+        }
+    }
+
+}
diff --git a/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/MidiInputPortSelector.java b/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/MidiInputPortSelector.java
new file mode 100644
index 0000000..7c665ba
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/MidiInputPortSelector.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2015 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.android.common.midi;
+
+import android.app.Activity;
+import android.media.midi.MidiDevice;
+import android.media.midi.MidiDeviceInfo;
+import android.media.midi.MidiInputPort;
+import android.media.midi.MidiManager;
+import android.media.midi.MidiReceiver;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+
+import java.io.IOException;
+
+/**
+ * Manages a Spinner for selecting a MidiInputPort.
+ */
+public class MidiInputPortSelector extends MidiPortSelector {
+
+    private MidiInputPort mInputPort;
+    private MidiDevice mOpenDevice;
+
+    /**
+     * @param midiManager
+     * @param activity
+     * @param spinnerId ID from the layout resource
+     */
+    public MidiInputPortSelector(MidiManager midiManager, Activity activity,
+            int spinnerId) {
+        super(midiManager, activity, spinnerId, MidiDeviceInfo.PortInfo.TYPE_INPUT);
+    }
+
+    @Override
+    public void onPortSelected(final MidiPortWrapper wrapper) {
+        close();
+        final MidiDeviceInfo info = wrapper.getDeviceInfo();
+        if (info != null) {
+            mMidiManager.openDevice(info, new MidiManager.OnDeviceOpenedListener() {
+                    @Override
+                public void onDeviceOpened(MidiDevice device) {
+                    if (device == null) {
+                        Log.e(MidiConstants.TAG, "could not open " + info);
+                    } else {
+                        mOpenDevice = device;
+                        mInputPort = mOpenDevice.openInputPort(
+                                wrapper.getPortIndex());
+                        if (mInputPort == null) {
+                            Log.e(MidiConstants.TAG, "could not open input port on " + info);
+                        }
+                    }
+                }
+            }, null);
+            // Don't run the callback on the UI thread because openInputPort might take a while.
+        }
+    }
+
+    public MidiReceiver getReceiver() {
+        return mInputPort;
+    }
+
+    @Override
+    public void onClose() {
+        try {
+            if (mInputPort != null) {
+                Log.i(MidiConstants.TAG, "MidiInputPortSelector.onClose() - close port");
+                mInputPort.close();
+            }
+            mInputPort = null;
+            if (mOpenDevice != null) {
+                mOpenDevice.close();
+            }
+            mOpenDevice = null;
+        } catch (IOException e) {
+            Log.e(MidiConstants.TAG, "cleanup failed", e);
+        }
+    }
+}
diff --git a/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/MidiOutputPortConnectionSelector.java b/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/MidiOutputPortConnectionSelector.java
new file mode 100644
index 0000000..ca1ade4
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/MidiOutputPortConnectionSelector.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2015 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.android.common.midi;
+
+import android.app.Activity;
+import android.media.midi.MidiDeviceInfo;
+import android.media.midi.MidiManager;
+import android.util.Log;
+
+import java.io.IOException;
+
+/**
+ * Select an output port and connect it to a destination input port.
+ */
+public class MidiOutputPortConnectionSelector extends MidiPortSelector {
+
+    private MidiPortConnector mSynthConnector;
+    private MidiDeviceInfo mDestinationDeviceInfo;
+    private int mDestinationPortIndex;
+    private MidiPortConnector.OnPortsConnectedListener mConnectedListener;
+
+    /**
+     * @param midiManager
+     * @param activity
+     * @param spinnerId
+     * @param type
+     */
+    public MidiOutputPortConnectionSelector(MidiManager midiManager,
+            Activity activity, int spinnerId,
+            MidiDeviceInfo destinationDeviceInfo, int destinationPortIndex) {
+        super(midiManager, activity, spinnerId,
+                MidiDeviceInfo.PortInfo.TYPE_OUTPUT);
+        mDestinationDeviceInfo = destinationDeviceInfo;
+        mDestinationPortIndex = destinationPortIndex;
+    }
+
+    @Override
+    public void onPortSelected(final MidiPortWrapper wrapper) {
+        Log.i(MidiConstants.TAG, "connectPortToSynth: " + wrapper);
+        onClose();
+        if (wrapper.getDeviceInfo() != null) {
+            mSynthConnector = new MidiPortConnector(mMidiManager);
+            mSynthConnector.connectToDevicePort(wrapper.getDeviceInfo(),
+                    wrapper.getPortIndex(), mDestinationDeviceInfo,
+                    mDestinationPortIndex,
+                    // not safe on UI thread
+                    mConnectedListener, null);
+        }
+    }
+
+    @Override
+    public void onClose() {
+        try {
+            if (mSynthConnector != null) {
+                mSynthConnector.close();
+                mSynthConnector = null;
+            }
+        } catch (IOException e) {
+            Log.e(MidiConstants.TAG, "Exception in closeSynthResources()", e);
+        }
+    }
+
+    /**
+     * @param myPortsConnectedListener
+     */
+    public void setConnectedListener(
+            MidiPortConnector.OnPortsConnectedListener connectedListener) {
+        mConnectedListener = connectedListener;
+    }
+}
diff --git a/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/MidiOutputPortSelector.java b/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/MidiOutputPortSelector.java
new file mode 100644
index 0000000..5aebf72
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/MidiOutputPortSelector.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2015 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.android.common.midi;
+
+import android.app.Activity;
+import android.media.midi.MidiDevice;
+import android.media.midi.MidiDeviceInfo;
+import android.media.midi.MidiManager;
+import android.media.midi.MidiOutputPort;
+import android.media.midi.MidiSender;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+
+import java.io.IOException;
+
+/**
+ * Manages a Spinner for selecting a MidiOutputPort.
+ */
+public class MidiOutputPortSelector extends MidiPortSelector {
+    private MidiOutputPort mOutputPort;
+    private MidiDispatcher mDispatcher = new MidiDispatcher();
+    private MidiDevice mOpenDevice;
+
+    /**
+     * @param midiManager
+     * @param activity
+     * @param spinnerId ID from the layout resource
+     */
+    public MidiOutputPortSelector(MidiManager midiManager, Activity activity,
+            int spinnerId) {
+        super(midiManager, activity, spinnerId, MidiDeviceInfo.PortInfo.TYPE_OUTPUT);
+    }
+
+    @Override
+    public void onPortSelected(final MidiPortWrapper wrapper) {
+        Log.i(MidiConstants.TAG, "onPortSelected: " + wrapper);
+        close();
+
+        final MidiDeviceInfo info = wrapper.getDeviceInfo();
+        if (info != null) {
+            mMidiManager.openDevice(info, new MidiManager.OnDeviceOpenedListener() {
+
+                    @Override
+                public void onDeviceOpened(MidiDevice device) {
+                    if (device == null) {
+                        Log.e(MidiConstants.TAG, "could not open " + info);
+                    } else {
+                        mOpenDevice = device;
+                        mOutputPort = device.openOutputPort(wrapper.getPortIndex());
+                        if (mOutputPort == null) {
+                            Log.e(MidiConstants.TAG,
+                                    "could not open output port for " + info);
+                            return;
+                        }
+                        mOutputPort.connect(mDispatcher);
+                    }
+                }
+            }, null);
+            // Don't run the callback on the UI thread because openOutputPort might take a while.
+        }
+    }
+
+    @Override
+    public void onClose() {
+        try {
+            if (mOutputPort != null) {
+                mOutputPort.disconnect(mDispatcher);
+            }
+            mOutputPort = null;
+            if (mOpenDevice != null) {
+                mOpenDevice.close();
+            }
+            mOpenDevice = null;
+        } catch (IOException e) {
+            Log.e(MidiConstants.TAG, "cleanup failed", e);
+        }
+    }
+
+    /**
+     * You can connect your MidiReceivers to this sender. The user will then select which output
+     * port will send messages through this MidiSender.
+     * @return a MidiSender that will send the messages from the selected port.
+     */
+    public MidiSender getSender() {
+        return mDispatcher.getSender();
+    }
+
+}
diff --git a/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/MidiPortConnector.java b/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/MidiPortConnector.java
new file mode 100644
index 0000000..457494d
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/MidiPortConnector.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2015 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.android.common.midi;
+
+import android.media.midi.MidiDevice;
+import android.media.midi.MidiDevice.MidiConnection;
+import android.media.midi.MidiDeviceInfo;
+import android.media.midi.MidiInputPort;
+import android.media.midi.MidiManager;
+import android.os.Handler;
+import android.util.Log;
+
+import java.io.IOException;
+
+/**
+ * Tool for connecting MIDI ports on two remote devices.
+ */
+public class MidiPortConnector {
+    private final MidiManager mMidiManager;
+    private MidiDevice mSourceDevice;
+    private MidiDevice mDestinationDevice;
+    private MidiConnection mConnection;
+
+    /**
+     * @param mMidiManager
+     */
+    public MidiPortConnector(MidiManager midiManager) {
+        mMidiManager = midiManager;
+    }
+
+    public void close() throws IOException {
+        if (mConnection != null) {
+            Log.i(MidiConstants.TAG,
+                    "MidiPortConnector closing connection " + mConnection);
+            mConnection.close();
+            mConnection = null;
+        }
+        if (mSourceDevice != null) {
+            mSourceDevice.close();
+            mSourceDevice = null;
+        }
+        if (mDestinationDevice != null) {
+            mDestinationDevice.close();
+            mDestinationDevice = null;
+        }
+    }
+
+    private void safeClose() {
+        try {
+            close();
+        } catch (IOException e) {
+            Log.e(MidiConstants.TAG, "could not close resources", e);
+        }
+    }
+
+    /**
+     * Listener class used for receiving the results of
+     * {@link #connectToDevicePort}
+     */
+    public interface OnPortsConnectedListener {
+        /**
+         * Called to respond to a {@link #connectToDevicePort} request
+         *
+         * @param connection
+         *            a {@link MidiConnection} that represents the connected
+         *            ports, or null if connection failed
+         */
+        abstract public void onPortsConnected(MidiConnection connection);
+    }
+
+    /**
+     * Open two devices and connect their ports.
+     *
+     * @param sourceDeviceInfo
+     * @param sourcePortIndex
+     * @param destinationDeviceInfo
+     * @param destinationPortIndex
+     */
+    public void connectToDevicePort(final MidiDeviceInfo sourceDeviceInfo,
+            final int sourcePortIndex,
+            final MidiDeviceInfo destinationDeviceInfo,
+            final int destinationPortIndex) {
+        connectToDevicePort(sourceDeviceInfo, sourcePortIndex,
+                destinationDeviceInfo, destinationPortIndex, null, null);
+    }
+
+    /**
+     * Open two devices and connect their ports.
+     *
+     * @param sourceDeviceInfo
+     * @param sourcePortIndex
+     * @param destinationDeviceInfo
+     * @param destinationPortIndex
+     */
+    public void connectToDevicePort(final MidiDeviceInfo sourceDeviceInfo,
+            final int sourcePortIndex,
+            final MidiDeviceInfo destinationDeviceInfo,
+            final int destinationPortIndex,
+            final OnPortsConnectedListener listener, final Handler handler) {
+        safeClose();
+        mMidiManager.openDevice(destinationDeviceInfo,
+                new MidiManager.OnDeviceOpenedListener() {
+                    @Override
+                    public void onDeviceOpened(MidiDevice destinationDevice) {
+                        if (destinationDevice == null) {
+                            Log.e(MidiConstants.TAG,
+                                    "could not open " + destinationDeviceInfo);
+                            if (listener != null) {
+                                listener.onPortsConnected(null);
+                            }
+                        } else {
+                            mDestinationDevice = destinationDevice;
+                            Log.i(MidiConstants.TAG,
+                                    "connectToDevicePort opened "
+                                            + destinationDeviceInfo);
+                            // Destination device was opened so go to next step.
+                            MidiInputPort destinationInputPort = destinationDevice
+                                    .openInputPort(destinationPortIndex);
+                            if (destinationInputPort != null) {
+                                Log.i(MidiConstants.TAG,
+                                        "connectToDevicePort opened port on "
+                                                + destinationDeviceInfo);
+                                connectToDevicePort(sourceDeviceInfo,
+                                        sourcePortIndex,
+                                        destinationInputPort,
+                                        listener, handler);
+                            } else {
+                                Log.e(MidiConstants.TAG,
+                                        "could not open port on "
+                                                + destinationDeviceInfo);
+                                safeClose();
+                                if (listener != null) {
+                                    listener.onPortsConnected(null);
+                                }
+                            }
+                        }
+                    }
+                }, handler);
+    }
+
+
+    /**
+     * Open a source device and connect its output port to the
+     * destinationInputPort.
+     *
+     * @param sourceDeviceInfo
+     * @param sourcePortIndex
+     * @param destinationInputPort
+     */
+    private void connectToDevicePort(final MidiDeviceInfo sourceDeviceInfo,
+            final int sourcePortIndex,
+            final MidiInputPort destinationInputPort,
+            final OnPortsConnectedListener listener, final Handler handler) {
+        mMidiManager.openDevice(sourceDeviceInfo,
+                new MidiManager.OnDeviceOpenedListener() {
+                    @Override
+                    public void onDeviceOpened(MidiDevice device) {
+                        if (device == null) {
+                            Log.e(MidiConstants.TAG,
+                                    "could not open " + sourceDeviceInfo);
+                            safeClose();
+                            if (listener != null) {
+                                listener.onPortsConnected(null);
+                            }
+                        } else {
+                            Log.i(MidiConstants.TAG,
+                                    "connectToDevicePort opened "
+                                            + sourceDeviceInfo);
+                            // Device was opened so connect the ports.
+                            mSourceDevice = device;
+                            mConnection = device.connectPorts(
+                                    destinationInputPort, sourcePortIndex);
+                            if (mConnection == null) {
+                                Log.e(MidiConstants.TAG, "could not connect to "
+                                        + sourceDeviceInfo);
+                                safeClose();
+                            }
+                            if (listener != null) {
+                                listener.onPortsConnected(mConnection);
+                            }
+                        }
+                    }
+                }, handler);
+    }
+
+}
diff --git a/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/MidiPortSelector.java b/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/MidiPortSelector.java
new file mode 100644
index 0000000..39f983e
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/MidiPortSelector.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2015 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.android.common.midi;
+
+import android.app.Activity;
+import android.media.midi.MidiDeviceInfo;
+import android.media.midi.MidiDeviceStatus;
+import android.media.midi.MidiManager;
+import android.media.midi.MidiManager.DeviceCallback;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Spinner;
+
+import java.util.HashSet;
+
+/**
+ * Base class that uses a Spinner to select available MIDI ports.
+ */
+public abstract class MidiPortSelector extends DeviceCallback {
+    private int mType = MidiDeviceInfo.PortInfo.TYPE_INPUT;
+    protected ArrayAdapter<MidiPortWrapper> mAdapter;
+    protected HashSet<MidiPortWrapper> mBusyPorts = new HashSet<MidiPortWrapper>();
+    private Spinner mSpinner;
+    protected MidiManager mMidiManager;
+    protected Activity mActivity;
+    private MidiPortWrapper mCurrentWrapper;
+
+    /**
+     * @param midiManager
+     * @param activity
+     * @param spinnerId
+     *            ID from the layout resource
+     * @param type
+     *            TYPE_INPUT or TYPE_OUTPUT
+     */
+    public MidiPortSelector(MidiManager midiManager, Activity activity,
+            int spinnerId, int type) {
+        mMidiManager = midiManager;
+        mActivity = activity;
+        mType = type;
+        mAdapter = new ArrayAdapter<MidiPortWrapper>(activity,
+                android.R.layout.simple_spinner_item);
+        mAdapter.setDropDownViewResource(
+                android.R.layout.simple_spinner_dropdown_item);
+        mAdapter.add(new MidiPortWrapper(null, 0, 0));
+
+        mSpinner = (Spinner) activity.findViewById(spinnerId);
+        mSpinner.setOnItemSelectedListener(
+                new AdapterView.OnItemSelectedListener() {
+
+                    public void onItemSelected(AdapterView<?> parent, View view,
+                            int pos, long id) {
+                        mCurrentWrapper = mAdapter.getItem(pos);
+                        onPortSelected(mCurrentWrapper);
+                    }
+
+                    public void onNothingSelected(AdapterView<?> parent) {
+                        onPortSelected(null);
+                        mCurrentWrapper = null;
+                    }
+                });
+        mSpinner.setAdapter(mAdapter);
+
+        mMidiManager.registerDeviceCallback(this,
+                new Handler(Looper.getMainLooper()));
+
+        MidiDeviceInfo[] infos = mMidiManager.getDevices();
+        for (MidiDeviceInfo info : infos) {
+            onDeviceAdded(info);
+        }
+    }
+
+    /**
+     * Set to no port selected.
+     */
+    public void clearSelection() {
+        mSpinner.setSelection(0);
+    }
+
+    private int getInfoPortCount(final MidiDeviceInfo info) {
+        int portCount = (mType == MidiDeviceInfo.PortInfo.TYPE_INPUT)
+                ? info.getInputPortCount() : info.getOutputPortCount();
+        return portCount;
+    }
+
+    @Override
+    public void onDeviceAdded(final MidiDeviceInfo info) {
+        int portCount = getInfoPortCount(info);
+        for (int i = 0; i < portCount; ++i) {
+            MidiPortWrapper wrapper = new MidiPortWrapper(info, mType, i);
+            mAdapter.add(wrapper);
+            Log.i(MidiConstants.TAG, wrapper + " was added");
+            mAdapter.notifyDataSetChanged();
+        }
+    }
+
+    @Override
+    public void onDeviceRemoved(final MidiDeviceInfo info) {
+        int portCount = getInfoPortCount(info);
+        for (int i = 0; i < portCount; ++i) {
+            MidiPortWrapper wrapper = new MidiPortWrapper(info, mType, i);
+            MidiPortWrapper currentWrapper = mCurrentWrapper;
+            mAdapter.remove(wrapper);
+            // If the currently selected port was removed then select no port.
+            if (wrapper.equals(currentWrapper)) {
+                clearSelection();
+            }
+            mAdapter.notifyDataSetChanged();
+            Log.i(MidiConstants.TAG, wrapper + " was removed");
+        }
+    }
+
+    @Override
+    public void onDeviceStatusChanged(final MidiDeviceStatus status) {
+        // If an input port becomes busy then remove it from the menu.
+        // If it becomes free then add it back to the menu.
+        if (mType == MidiDeviceInfo.PortInfo.TYPE_INPUT) {
+            MidiDeviceInfo info = status.getDeviceInfo();
+            Log.i(MidiConstants.TAG, "MidiPortSelector.onDeviceStatusChanged status = " + status
+                    + ", mType = " + mType
+                    + ", activity = " + mActivity.getPackageName()
+                    + ", info = " + info);
+            // Look for transitions from free to busy.
+            int portCount = info.getInputPortCount();
+            for (int i = 0; i < portCount; ++i) {
+                MidiPortWrapper wrapper = new MidiPortWrapper(info, mType, i);
+                if (!wrapper.equals(mCurrentWrapper)) {
+                    if (status.isInputPortOpen(i)) { // busy?
+                        if (!mBusyPorts.contains(wrapper)) {
+                            // was free, now busy
+                            mBusyPorts.add(wrapper);
+                            mAdapter.remove(wrapper);
+                            mAdapter.notifyDataSetChanged();
+                        }
+                    } else {
+                        if (mBusyPorts.remove(wrapper)) {
+                            // was busy, now free
+                            mAdapter.add(wrapper);
+                            mAdapter.notifyDataSetChanged();
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Implement this method to handle the user selecting a port on a device.
+     *
+     * @param wrapper
+     */
+    public abstract void onPortSelected(MidiPortWrapper wrapper);
+
+    /**
+     * Implement this method to clean up any open resources.
+     */
+    public abstract void onClose();
+
+    /**
+     *
+     */
+    public void close() {
+        onClose();
+    }
+}
diff --git a/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/MidiPortWrapper.java b/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/MidiPortWrapper.java
new file mode 100644
index 0000000..77aa734
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/MidiPortWrapper.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2015 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.android.common.midi;
+
+import android.media.midi.MidiDeviceInfo;
+import android.media.midi.MidiDeviceInfo.PortInfo;
+import android.util.Log;
+
+// Wrapper for a MIDI device and port description.
+public class MidiPortWrapper {
+    private MidiDeviceInfo mInfo;
+    private int mPortIndex;
+    private int mType;
+    private String mString;
+
+    /**
+     * Wrapper for a MIDI device and port description.
+     * @param info
+     * @param portType
+     * @param portIndex
+     */
+    public MidiPortWrapper(MidiDeviceInfo info, int portType, int portIndex) {
+        mInfo = info;
+        mType = portType;
+        mPortIndex = portIndex;
+    }
+
+    private void updateString() {
+        if (mInfo == null) {
+            mString = "- - - - - -";
+        } else {
+            StringBuilder sb = new StringBuilder();
+            String name = mInfo.getProperties()
+                    .getString(MidiDeviceInfo.PROPERTY_NAME);
+            if (name == null) {
+                name = mInfo.getProperties()
+                        .getString(MidiDeviceInfo.PROPERTY_MANUFACTURER) + ", "
+                        + mInfo.getProperties()
+                                .getString(MidiDeviceInfo.PROPERTY_PRODUCT);
+            }
+            sb.append("#" + mInfo.getId());
+            sb.append(", ").append(name);
+            PortInfo portInfo = findPortInfo();
+            sb.append("[" + mPortIndex + "]");
+            if (portInfo != null) {
+                sb.append(", ").append(portInfo.getName());
+            } else {
+                sb.append(", null");
+            }
+            mString = sb.toString();
+        }
+    }
+
+    /**
+     * @param info
+     * @param portIndex
+     * @return
+     */
+    private PortInfo findPortInfo() {
+        PortInfo[] ports = mInfo.getPorts();
+        for (PortInfo portInfo : ports) {
+            if (portInfo.getPortNumber() == mPortIndex
+                    && portInfo.getType() == mType) {
+                return portInfo;
+            }
+        }
+        return null;
+    }
+
+    public int getPortIndex() {
+        return mPortIndex;
+    }
+
+    public MidiDeviceInfo getDeviceInfo() {
+        return mInfo;
+    }
+
+    @Override
+    public String toString() {
+        if (mString == null) {
+            updateString();
+        }
+        return mString;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (other == null)
+            return false;
+        if (!(other instanceof MidiPortWrapper))
+            return false;
+        MidiPortWrapper otherWrapper = (MidiPortWrapper) other;
+        if (mPortIndex != otherWrapper.mPortIndex)
+            return false;
+        if (mType != otherWrapper.mType)
+            return false;
+        if (mInfo == null)
+            return (otherWrapper.mInfo == null);
+        return mInfo.equals(otherWrapper.mInfo);
+    }
+
+    @Override
+    public int hashCode() {
+        int hashCode = 1;
+        hashCode = 31 * hashCode + mPortIndex;
+        hashCode = 31 * hashCode + mType;
+        hashCode = 31 * hashCode + mInfo.hashCode();
+        return hashCode;
+    }
+
+}
diff --git a/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/MidiTools.java b/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/MidiTools.java
new file mode 100644
index 0000000..82e3de4
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/MidiTools.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2015 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.android.common.midi;
+
+import android.media.midi.MidiDeviceInfo;
+import android.media.midi.MidiManager;
+
+/**
+ * Miscellaneous tools for Android MIDI.
+ */
+public class MidiTools {
+
+    /**
+     * @return a device that matches the manufacturer and product or null
+     */
+    public static MidiDeviceInfo findDevice(MidiManager midiManager,
+            String manufacturer, String product) {
+        for (MidiDeviceInfo info : midiManager.getDevices()) {
+            String deviceManufacturer = info.getProperties()
+                    .getString(MidiDeviceInfo.PROPERTY_MANUFACTURER);
+            if ((manufacturer != null)
+                    && manufacturer.equals(deviceManufacturer)) {
+                String deviceProduct = info.getProperties()
+                        .getString(MidiDeviceInfo.PROPERTY_PRODUCT);
+                if ((product != null) && product.equals(deviceProduct)) {
+                    return info;
+                }
+            }
+        }
+        return null;
+    }
+}
diff --git a/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/synth/EnvelopeADSR.java b/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/synth/EnvelopeADSR.java
new file mode 100644
index 0000000..a29a193
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/synth/EnvelopeADSR.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2015 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.android.common.midi.synth;
+
+/**
+ * Very simple Attack, Decay, Sustain, Release envelope with linear ramps.
+ *
+ * Times are in seconds.
+ */
+public class EnvelopeADSR extends SynthUnit {
+    private static final int IDLE = 0;
+    private static final int ATTACK = 1;
+    private static final int DECAY = 2;
+    private static final int SUSTAIN = 3;
+    private static final int RELEASE = 4;
+    private static final int FINISHED = 5;
+    private static final float MIN_TIME = 0.001f;
+
+    private float mAttackRate;
+    private float mRreleaseRate;
+    private float mSustainLevel;
+    private float mDecayRate;
+    private float mCurrent;
+    private int mSstate = IDLE;
+
+    public EnvelopeADSR() {
+        setAttackTime(0.003f);
+        setDecayTime(0.08f);
+        setSustainLevel(0.3f);
+        setReleaseTime(1.0f);
+    }
+
+    public void setAttackTime(float time) {
+        if (time < MIN_TIME)
+            time = MIN_TIME;
+        mAttackRate = 1.0f / (SynthEngine.FRAME_RATE * time);
+    }
+
+    public void setDecayTime(float time) {
+        if (time < MIN_TIME)
+            time = MIN_TIME;
+        mDecayRate = 1.0f / (SynthEngine.FRAME_RATE * time);
+    }
+
+    public void setSustainLevel(float level) {
+        if (level < 0.0f)
+            level = 0.0f;
+        mSustainLevel = level;
+    }
+
+    public void setReleaseTime(float time) {
+        if (time < MIN_TIME)
+            time = MIN_TIME;
+        mRreleaseRate = 1.0f / (SynthEngine.FRAME_RATE * time);
+    }
+
+    public void on() {
+        mSstate = ATTACK;
+    }
+
+    public void off() {
+        mSstate = RELEASE;
+    }
+
+    @Override
+    public float render() {
+        switch (mSstate) {
+        case ATTACK:
+            mCurrent += mAttackRate;
+            if (mCurrent > 1.0f) {
+                mCurrent = 1.0f;
+                mSstate = DECAY;
+            }
+            break;
+        case DECAY:
+            mCurrent -= mDecayRate;
+            if (mCurrent < mSustainLevel) {
+                mCurrent = mSustainLevel;
+                mSstate = SUSTAIN;
+            }
+            break;
+        case RELEASE:
+            mCurrent -= mRreleaseRate;
+            if (mCurrent < 0.0f) {
+                mCurrent = 0.0f;
+                mSstate = FINISHED;
+            }
+            break;
+        }
+        return mCurrent;
+    }
+
+    public boolean isDone() {
+        return mSstate == FINISHED;
+    }
+
+}
diff --git a/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/synth/SawOscillator.java b/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/synth/SawOscillator.java
new file mode 100644
index 0000000..c02a6a1
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/synth/SawOscillator.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2015 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.android.common.midi.synth;
+
+public class SawOscillator extends SynthUnit {
+    private float mPhase = 0.0f;
+    private float mPhaseIncrement = 0.01f;
+    private float mFrequency = 0.0f;
+    private float mFrequencyScaler = 1.0f;
+    private float mAmplitude = 1.0f;
+
+    public void setPitch(float pitch) {
+        float freq = (float) pitchToFrequency(pitch);
+        setFrequency(freq);
+    }
+
+    public void setFrequency(float frequency) {
+        mFrequency = frequency;
+        updatePhaseIncrement();
+    }
+
+    private void updatePhaseIncrement() {
+        mPhaseIncrement = 2.0f * mFrequency * mFrequencyScaler / 48000.0f;
+    }
+
+    public void setAmplitude(float amplitude) {
+        mAmplitude = amplitude;
+    }
+
+    public float getAmplitude() {
+        return mAmplitude;
+    }
+
+    public float getFrequencyScaler() {
+        return mFrequencyScaler;
+    }
+
+    public void setFrequencyScaler(float frequencyScaler) {
+        mFrequencyScaler = frequencyScaler;
+        updatePhaseIncrement();
+    }
+
+    float incrementWrapPhase() {
+        mPhase += mPhaseIncrement;
+        while (mPhase > 1.0) {
+            mPhase -= 2.0;
+        }
+        while (mPhase < -1.0) {
+            mPhase += 2.0;
+        }
+        return mPhase;
+    }
+
+    @Override
+    public float render() {
+        return incrementWrapPhase() * mAmplitude;
+    }
+
+}
diff --git a/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/synth/SawOscillatorDPW.java b/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/synth/SawOscillatorDPW.java
new file mode 100644
index 0000000..e5d661d
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/synth/SawOscillatorDPW.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2015 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.android.common.midi.synth;
+
+/**
+ * Band limited sawtooth oscillator.
+ * This will have very little aliasing at high frequencies.
+ */
+public class SawOscillatorDPW extends SawOscillator {
+    private float mZ1 = 0.0f; // delayed values
+    private float mZ2 = 0.0f;
+    private float mScaler; // frequency dependent scaler
+    private final static float VERY_LOW_FREQ = 0.0000001f;
+
+    @Override
+    public void setFrequency(float freq) {
+        /* Calculate scaling based on frequency. */
+        freq = Math.abs(freq);
+        super.setFrequency(freq);
+        if (freq < VERY_LOW_FREQ) {
+            mScaler = (float) (0.125 * 44100 / VERY_LOW_FREQ);
+        } else {
+            mScaler = (float) (0.125 * 44100 / freq);
+        }
+    }
+
+    @Override
+    public float render() {
+        float phase = incrementWrapPhase();
+        /* Square the raw sawtooth. */
+        float squared = phase * phase;
+        float diffed = squared - mZ2;
+        mZ2 = mZ1;
+        mZ1 = squared;
+        return diffed * mScaler * getAmplitude();
+    }
+
+}
diff --git a/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/synth/SawVoice.java b/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/synth/SawVoice.java
new file mode 100644
index 0000000..3b3e543
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/synth/SawVoice.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2015 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.android.common.midi.synth;
+
+/**
+ * Sawtooth oscillator with an ADSR.
+ */
+public class SawVoice extends SynthVoice {
+    private SawOscillator mOscillator;
+    private EnvelopeADSR mEnvelope;
+
+    public SawVoice() {
+        mOscillator = createOscillator();
+        mEnvelope = new EnvelopeADSR();
+    }
+
+    protected SawOscillator createOscillator() {
+        return new SawOscillator();
+    }
+
+    @Override
+    public void noteOn(int noteIndex, int velocity) {
+        super.noteOn(noteIndex, velocity);
+        mOscillator.setPitch(noteIndex);
+        mOscillator.setAmplitude(getAmplitude());
+        mEnvelope.on();
+    }
+
+    @Override
+    public void noteOff() {
+        super.noteOff();
+        mEnvelope.off();
+    }
+
+    @Override
+    public void setFrequencyScaler(float scaler) {
+        mOscillator.setFrequencyScaler(scaler);
+    }
+
+    @Override
+    public float render() {
+        float output = mOscillator.render() * mEnvelope.render();
+        return output;
+    }
+
+    @Override
+    public boolean isDone() {
+        return mEnvelope.isDone();
+    }
+
+}
diff --git a/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/synth/SimpleAudioOutput.java b/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/synth/SimpleAudioOutput.java
new file mode 100644
index 0000000..04aa19c
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/synth/SimpleAudioOutput.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2015 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.android.common.midi.synth;
+
+import android.media.AudioFormat;
+import android.media.AudioManager;
+import android.media.AudioTrack;
+import android.util.Log;
+
+/**
+ * Simple base class for implementing audio output for examples.
+ * This can be sub-classed for experimentation or to redirect audio output.
+ */
+public class SimpleAudioOutput {
+
+    private static final String TAG = "AudioOutputTrack";
+    public static final int SAMPLES_PER_FRAME = 2;
+    public static final int BYTES_PER_SAMPLE = 4; // float
+    public static final int BYTES_PER_FRAME = SAMPLES_PER_FRAME * BYTES_PER_SAMPLE;
+    private AudioTrack mAudioTrack;
+    private int mFrameRate;
+
+    /**
+     *
+     */
+    public SimpleAudioOutput() {
+        super();
+    }
+
+    /**
+     * Create an audio track then call play().
+     *
+     * @param frameRate
+     */
+    public void start(int frameRate) {
+        stop();
+        mFrameRate = frameRate;
+        mAudioTrack = createAudioTrack(frameRate);
+        // AudioTrack will wait until it has enough data before starting.
+        mAudioTrack.play();
+    }
+
+    public AudioTrack createAudioTrack(int frameRate) {
+        int minBufferSizeBytes = AudioTrack.getMinBufferSize(frameRate,
+                AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_FLOAT);
+        Log.i(TAG, "AudioTrack.minBufferSize = " + minBufferSizeBytes
+                + " bytes = " + (minBufferSizeBytes / BYTES_PER_FRAME)
+                + " frames");
+        int bufferSize = 8 * minBufferSizeBytes / 8;
+        int outputBufferSizeFrames = bufferSize / BYTES_PER_FRAME;
+        Log.i(TAG, "actual bufferSize = " + bufferSize + " bytes = "
+                + outputBufferSizeFrames + " frames");
+
+        AudioTrack player = new AudioTrack(AudioManager.STREAM_MUSIC,
+                mFrameRate, AudioFormat.CHANNEL_OUT_STEREO,
+                AudioFormat.ENCODING_PCM_FLOAT, bufferSize,
+                AudioTrack.MODE_STREAM);
+        Log.i(TAG, "created AudioTrack");
+        return player;
+    }
+
+    public int write(float[] buffer, int offset, int length) {
+        return mAudioTrack.write(buffer, offset, length,
+                AudioTrack.WRITE_BLOCKING);
+    }
+
+    public void stop() {
+        if (mAudioTrack != null) {
+            mAudioTrack.stop();
+            mAudioTrack = null;
+        }
+    }
+
+    public int getFrameRate() {
+        return mFrameRate;
+    }
+
+    public AudioTrack getAudioTrack() {
+        return mAudioTrack;
+    }
+}
diff --git a/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/synth/SineOscillator.java b/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/synth/SineOscillator.java
new file mode 100644
index 0000000..c638c34
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/synth/SineOscillator.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2015 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.android.common.midi.synth;
+
+/**
+ * Sinewave oscillator.
+ */
+public class SineOscillator extends SawOscillator {
+    // Factorial constants.
+    private static final float IF3 = 1.0f / (2 * 3);
+    private static final float IF5 = IF3 / (4 * 5);
+    private static final float IF7 = IF5 / (6 * 7);
+    private static final float IF9 = IF7 / (8 * 9);
+    private static final float IF11 = IF9 / (10 * 11);
+
+    /**
+     * Calculate sine using Taylor expansion. Do not use values outside the range.
+     *
+     * @param currentPhase in the range of -1.0 to +1.0 for one cycle
+     */
+    public static float fastSin(float currentPhase) {
+
+        /* Wrap phase back into region where results are more accurate. */
+        float yp = (currentPhase > 0.5f) ? 1.0f - currentPhase 
+                : ((currentPhase < (-0.5f)) ? (-1.0f) - currentPhase : currentPhase);
+
+        float x = (float) (yp * Math.PI);
+        float x2 = (x * x);
+        /* Taylor expansion out to x**11/11! factored into multiply-adds */
+        return x * (x2 * (x2 * (x2 * (x2 * ((x2 * (-IF11)) + IF9) - IF7) + IF5) - IF3) + 1);
+    }
+
+    @Override
+    public float render() {
+        // Convert raw sawtooth to sine.
+        float phase = incrementWrapPhase();
+        return fastSin(phase) * getAmplitude();
+    }
+
+}
diff --git a/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/synth/SineVoice.java b/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/synth/SineVoice.java
new file mode 100644
index 0000000..e80d2c7
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/synth/SineVoice.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2015 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.android.common.midi.synth;
+
+/**
+ * Replace sawtooth with a sine wave.
+ */
+public class SineVoice extends SawVoice {
+    @Override
+    protected SawOscillator createOscillator() {
+        return new SineOscillator();
+    }
+}
diff --git a/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/synth/SynthEngine.java b/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/synth/SynthEngine.java
new file mode 100644
index 0000000..6cd02a6
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/synth/SynthEngine.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2015 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.android.common.midi.synth;
+
+import android.media.midi.MidiReceiver;
+import android.util.Log;
+
+import com.example.android.common.midi.MidiConstants;
+import com.example.android.common.midi.MidiEventScheduler;
+import com.example.android.common.midi.MidiEventScheduler.MidiEvent;
+import com.example.android.common.midi.MidiFramer;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.Iterator;
+
+/**
+ * Very simple polyphonic, single channel synthesizer. It runs a background
+ * thread that processes MIDI events and synthesizes audio.
+ */
+public class SynthEngine extends MidiReceiver {
+
+    private static final String TAG = "SynthEngine";
+
+    public static final int FRAME_RATE = 48000;
+    private static final int FRAMES_PER_BUFFER = 240;
+    private static final int SAMPLES_PER_FRAME = 2;
+
+    private boolean go;
+    private Thread mThread;
+    private float[] mBuffer = new float[FRAMES_PER_BUFFER * SAMPLES_PER_FRAME];
+    private float mFrequencyScaler = 1.0f;
+    private float mBendRange = 2.0f; // semitones
+    private int mProgram;
+
+    private ArrayList<SynthVoice> mFreeVoices = new ArrayList<SynthVoice>();
+    private Hashtable<Integer, SynthVoice>
+            mVoices = new Hashtable<Integer, SynthVoice>();
+    private MidiEventScheduler mEventScheduler;
+    private MidiFramer mFramer;
+    private MidiReceiver mReceiver = new MyReceiver();
+    private SimpleAudioOutput mAudioOutput;
+
+    public SynthEngine() {
+        this(new SimpleAudioOutput());
+    }
+
+    public SynthEngine(SimpleAudioOutput audioOutput) {
+        mReceiver = new MyReceiver();
+        mFramer = new MidiFramer(mReceiver);
+        mAudioOutput = audioOutput;
+    }
+
+    @Override
+    public void onSend(byte[] data, int offset, int count, long timestamp)
+            throws IOException {
+        if (mEventScheduler != null) {
+            if (!MidiConstants.isAllActiveSensing(data, offset, count)) {
+                mEventScheduler.getReceiver().send(data, offset, count,
+                        timestamp);
+            }
+        }
+    }
+
+    private class MyReceiver extends MidiReceiver {
+        @Override
+        public void onSend(byte[] data, int offset, int count, long timestamp)
+                throws IOException {
+            byte command = (byte) (data[0] & MidiConstants.STATUS_COMMAND_MASK);
+            int channel = (byte) (data[0] & MidiConstants.STATUS_CHANNEL_MASK);
+            switch (command) {
+            case MidiConstants.STATUS_NOTE_OFF:
+                noteOff(channel, data[1], data[2]);
+                break;
+            case MidiConstants.STATUS_NOTE_ON:
+                noteOn(channel, data[1], data[2]);
+                break;
+            case MidiConstants.STATUS_PITCH_BEND:
+                int bend = (data[2] << 7) + data[1];
+                pitchBend(channel, bend);
+                break;
+            case MidiConstants.STATUS_PROGRAM_CHANGE:
+                mProgram = data[1];
+                mFreeVoices.clear();
+                break;
+            default:
+                logMidiMessage(data, offset, count);
+                break;
+            }
+        }
+    }
+
+    class MyRunnable implements Runnable {
+        @Override
+        public void run() {
+            try {
+                mAudioOutput.start(FRAME_RATE);
+                onLoopStarted();
+                while (go) {
+                    processMidiEvents();
+                    generateBuffer();
+                    mAudioOutput.write(mBuffer, 0, mBuffer.length);
+                    onBufferCompleted(FRAMES_PER_BUFFER);
+                }
+            } catch (Exception e) {
+                Log.e(TAG, "SynthEngine background thread exception.", e);
+            } finally {
+                onLoopEnded();
+                mAudioOutput.stop();
+            }
+        }
+    }
+
+    /**
+     * This is called form the synthesis thread before it starts looping.
+     */
+    public void onLoopStarted() {
+    }
+
+    /**
+     * This is called once at the end of each synthesis loop.
+     *
+     * @param framesPerBuffer
+     */
+    public void onBufferCompleted(int framesPerBuffer) {
+    }
+
+    /**
+     * This is called form the synthesis thread when it stop looping.
+     */
+    public void onLoopEnded() {
+    }
+
+    /**
+     * Assume message has been aligned to the start of a MIDI message.
+     *
+     * @param data
+     * @param offset
+     * @param count
+     */
+    public void logMidiMessage(byte[] data, int offset, int count) {
+        String text = "Received: ";
+        for (int i = 0; i < count; i++) {
+            text += String.format("0x%02X, ", data[offset + i]);
+        }
+        Log.i(TAG, text);
+    }
+
+    /**
+     * @throws IOException
+     *
+     */
+    private void processMidiEvents() throws IOException {
+        long now = System.nanoTime(); // TODO use audio presentation time
+        MidiEvent event = (MidiEvent) mEventScheduler.getNextEvent(now);
+        while (event != null) {
+            mFramer.send(event.data, 0, event.count, event.getTimestamp());
+            mEventScheduler.addEventToPool(event);
+            event = (MidiEvent) mEventScheduler.getNextEvent(now);
+        }
+    }
+
+    /**
+     *
+     */
+    private void generateBuffer() {
+        for (int i = 0; i < mBuffer.length; i++) {
+            mBuffer[i] = 0.0f;
+        }
+        Iterator<SynthVoice> iterator = mVoices.values().iterator();
+        while (iterator.hasNext()) {
+            SynthVoice voice = iterator.next();
+            if (voice.isDone()) {
+                iterator.remove();
+                // mFreeVoices.add(voice);
+            } else {
+                voice.mix(mBuffer, SAMPLES_PER_FRAME, 0.25f);
+            }
+        }
+    }
+
+    public void noteOff(int channel, int noteIndex, int velocity) {
+        SynthVoice voice = mVoices.get(noteIndex);
+        if (voice != null) {
+            voice.noteOff();
+        }
+    }
+
+    public void allNotesOff() {
+        Iterator<SynthVoice> iterator = mVoices.values().iterator();
+        while (iterator.hasNext()) {
+            SynthVoice voice = iterator.next();
+            voice.noteOff();
+        }
+    }
+
+    /**
+     * Create a SynthVoice.
+     */
+    public SynthVoice createVoice(int program) {
+        // For every odd program number use a sine wave.
+        if ((program & 1) == 1) {
+            return new SineVoice();
+        } else {
+            return new SawVoice();
+        }
+    }
+
+    /**
+     *
+     * @param channel
+     * @param noteIndex
+     * @param velocity
+     */
+    public void noteOn(int channel, int noteIndex, int velocity) {
+        if (velocity == 0) {
+            noteOff(channel, noteIndex, velocity);
+        } else {
+            mVoices.remove(noteIndex);
+            SynthVoice voice;
+            if (mFreeVoices.size() > 0) {
+                voice = mFreeVoices.remove(mFreeVoices.size() - 1);
+            } else {
+                voice = createVoice(mProgram);
+            }
+            voice.setFrequencyScaler(mFrequencyScaler);
+            voice.noteOn(noteIndex, velocity);
+            mVoices.put(noteIndex, voice);
+        }
+    }
+
+    public void pitchBend(int channel, int bend) {
+        double semitones = (mBendRange * (bend - 0x2000)) / 0x2000;
+        mFrequencyScaler = (float) Math.pow(2.0, semitones / 12.0);
+        Iterator<SynthVoice> iterator = mVoices.values().iterator();
+        while (iterator.hasNext()) {
+            SynthVoice voice = iterator.next();
+            voice.setFrequencyScaler(mFrequencyScaler);
+        }
+    }
+
+    /**
+     * Start the synthesizer.
+     */
+    public void start() {
+        stop();
+        go = true;
+        mThread = new Thread(new MyRunnable());
+        mEventScheduler = new MidiEventScheduler();
+        mThread.start();
+    }
+
+    /**
+     * Stop the synthesizer.
+     */
+    public void stop() {
+        go = false;
+        if (mThread != null) {
+            try {
+                mThread.interrupt();
+                mThread.join(500);
+            } catch (InterruptedException e) {
+                // OK, just stopping safely.
+            }
+            mThread = null;
+            mEventScheduler = null;
+        }
+    }
+}
diff --git a/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/synth/SynthUnit.java b/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/synth/SynthUnit.java
new file mode 100644
index 0000000..90599e2
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/synth/SynthUnit.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2015 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.android.common.midi.synth;
+
+public abstract class SynthUnit {
+
+    private static final double CONCERT_A_PITCH = 69.0;
+    private static final double CONCERT_A_FREQUENCY = 440.0;
+
+    /**
+     * @param pitch
+     *            MIDI pitch in semitones
+     * @return frequency
+     */
+    public static double pitchToFrequency(double pitch) {
+        double semitones = pitch - CONCERT_A_PITCH;
+        return CONCERT_A_FREQUENCY * Math.pow(2.0, semitones / 12.0);
+    }
+
+    public abstract float render();
+}
diff --git a/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/synth/SynthVoice.java b/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/synth/SynthVoice.java
new file mode 100644
index 0000000..78ba09a
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/common/midi/synth/SynthVoice.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2015 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.android.common.midi.synth;
+
+/**
+ * Base class for a polyphonic synthesizer voice.
+ */
+public abstract class SynthVoice {
+    private int mNoteIndex;
+    private float mAmplitude;
+    public static final int STATE_OFF = 0;
+    public static final int STATE_ON = 1;
+    private int mState = STATE_OFF;
+
+    public SynthVoice() {
+        mNoteIndex = -1;
+    }
+
+    public void noteOn(int noteIndex, int velocity) {
+        mState = STATE_ON;
+        this.mNoteIndex = noteIndex;
+        setAmplitude(velocity / 128.0f);
+    }
+
+    public void noteOff() {
+        mState = STATE_OFF;
+    }
+
+    /**
+     * Add the output of this voice to an output buffer.
+     *
+     * @param outputBuffer
+     * @param samplesPerFrame
+     * @param level
+     */
+    public void mix(float[] outputBuffer, int samplesPerFrame, float level) {
+        int numFrames = outputBuffer.length / samplesPerFrame;
+        for (int i = 0; i < numFrames; i++) {
+            float output = render();
+            int offset = i * samplesPerFrame;
+            for (int jf = 0; jf < samplesPerFrame; jf++) {
+                outputBuffer[offset + jf] += output * level;
+            }
+        }
+    }
+
+    public abstract float render();
+
+    public boolean isDone() {
+        return mState == STATE_OFF;
+    }
+
+    public int getNoteIndex() {
+        return mNoteIndex;
+    }
+
+    public float getAmplitude() {
+        return mAmplitude;
+    }
+
+    public void setAmplitude(float amplitude) {
+        this.mAmplitude = amplitude;
+    }
+
+    /**
+     * @param scaler
+     */
+    public void setFrequencyScaler(float scaler) {
+    }
+
+}
diff --git a/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/midiscope/LoggingReceiver.java b/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/midiscope/LoggingReceiver.java
new file mode 100644
index 0000000..23ce8f7
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/midiscope/LoggingReceiver.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2015 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.android.midiscope;
+
+import android.media.midi.MidiReceiver;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Convert incoming MIDI messages to a string and write them to a ScopeLogger.
+ * Assume that messages have been aligned using a MidiFramer.
+ */
+public class LoggingReceiver extends MidiReceiver {
+    public static final String TAG = "MidiScope";
+    private static final long NANOS_PER_SECOND = TimeUnit.SECONDS.toNanos(1);
+    private long mStartTime;
+    private ScopeLogger mLogger;
+
+    public LoggingReceiver(ScopeLogger logger) {
+        mStartTime = System.nanoTime();
+        mLogger = logger;
+    }
+
+    /*
+     * @see android.media.midi.MidiReceiver#onReceive(byte[], int, int, long)
+     */
+    @Override
+    public void onSend(byte[] data, int offset, int count, long timestamp)
+            throws IOException {
+        StringBuilder sb = new StringBuilder();
+        if (timestamp == 0) {
+            sb.append(String.format("-----0----: "));
+        } else {
+            long monoTime = timestamp - mStartTime;
+            double seconds = (double) monoTime / NANOS_PER_SECOND;
+            sb.append(String.format("%10.3f: ", seconds));
+        }
+        sb.append(MidiPrinter.formatBytes(data, offset, count));
+        sb.append(": ");
+        sb.append(MidiPrinter.formatMessage(data, offset, count));
+        String text = sb.toString();
+        mLogger.log(text);
+        Log.i(TAG, text);
+    }
+
+}
\ No newline at end of file
diff --git a/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/midiscope/MainActivity.java b/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/midiscope/MainActivity.java
new file mode 100644
index 0000000..41d74f0
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/midiscope/MainActivity.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2015 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.android.midiscope;
+
+import android.app.ActionBar;
+import android.app.Activity;
+import android.media.midi.MidiDeviceInfo;
+import android.media.midi.MidiManager;
+import android.media.midi.MidiReceiver;
+import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.ScrollView;
+import android.widget.TextView;
+import android.widget.Toolbar;
+
+import com.example.android.common.midi.MidiFramer;
+import com.example.android.common.midi.MidiOutputPortSelector;
+import com.example.android.common.midi.MidiPortWrapper;
+
+import java.util.LinkedList;
+
+/**
+ * App that provides a MIDI echo service.
+ */
+public class MainActivity extends Activity implements ScopeLogger {
+
+    private static final int MAX_LINES = 100;
+
+    private final LinkedList<String> mLogLines = new LinkedList<>();
+    private TextView mLog;
+    private ScrollView mScroller;
+    private MidiOutputPortSelector mLogSenderSelector;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main);
+
+        setActionBar((Toolbar) findViewById(R.id.toolbar));
+        ActionBar actionBar = getActionBar();
+        if (actionBar != null) {
+            actionBar.setDisplayShowTitleEnabled(false);
+        }
+
+        mLog = (TextView) findViewById(R.id.log);
+        mScroller = (ScrollView) findViewById(R.id.scroll);
+
+        // Setup MIDI
+        MidiManager midiManager = (MidiManager) getSystemService(MIDI_SERVICE);
+
+        // Receiver that prints the messages.
+        MidiReceiver loggingReceiver = new LoggingReceiver(this);
+
+        // Receiver that parses raw data into complete messages.
+        MidiFramer connectFramer = new MidiFramer(loggingReceiver);
+
+        // Setup a menu to select an input source.
+        mLogSenderSelector = new MidiOutputPortSelector(midiManager, this, R.id.spinner_senders) {
+            @Override
+            public void onPortSelected(final MidiPortWrapper wrapper) {
+                super.onPortSelected(wrapper);
+                if (wrapper != null) {
+                    mLogLines.clear();
+                    MidiDeviceInfo deviceInfo = wrapper.getDeviceInfo();
+                    if (deviceInfo == null) {
+                        log(getString(R.string.header_text));
+                    } else {
+                        log(MidiPrinter.formatDeviceInfo(deviceInfo));
+                    }
+                }
+            }
+        };
+        mLogSenderSelector.getSender().connect(connectFramer);
+
+        // Tell the virtual device to log its messages here..
+        MidiScope.setScopeLogger(this);
+    }
+
+    @Override
+    public void onDestroy() {
+        mLogSenderSelector.onClose();
+        // The scope will live on as a service so we need to tell it to stop
+        // writing log messages to this Activity.
+        MidiScope.setScopeLogger(null);
+        super.onDestroy();
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        getMenuInflater().inflate(R.menu.main, menu);
+        setKeepScreenOn(menu.findItem(R.id.action_keep_screen_on).isChecked());
+        return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case R.id.action_clear_all:
+                mLogLines.clear();
+                logOnUiThread("");
+                break;
+            case R.id.action_keep_screen_on:
+                boolean checked = !item.isChecked();
+                setKeepScreenOn(checked);
+                item.setChecked(checked);
+                break;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    private void setKeepScreenOn(boolean keepScreenOn) {
+        if (keepScreenOn) {
+            getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+        } else {
+            getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+        }
+    }
+
+    @Override
+    public void log(final String string) {
+        runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                logOnUiThread(string);
+            }
+        });
+    }
+
+    /**
+     * Logs a message to our TextView. This needs to be called from the UI thread.
+     */
+    private void logOnUiThread(String s) {
+        mLogLines.add(s);
+        if (mLogLines.size() > MAX_LINES) {
+            mLogLines.removeFirst();
+        }
+        // Render line buffer to one String.
+        StringBuilder sb = new StringBuilder();
+        for (String line : mLogLines) {
+            sb.append(line).append('\n');
+        }
+        mLog.setText(sb.toString());
+        mScroller.fullScroll(View.FOCUS_DOWN);
+    }
+}
diff --git a/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/midiscope/MidiPrinter.java b/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/midiscope/MidiPrinter.java
new file mode 100644
index 0000000..9e97c04
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/midiscope/MidiPrinter.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2015 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.android.midiscope;
+
+import android.media.midi.MidiDeviceInfo;
+import android.media.midi.MidiDeviceInfo.PortInfo;
+import android.os.Bundle;
+
+import com.example.android.common.midi.MidiConstants;
+
+/**
+ * Format a MIDI message for printing.
+ */
+public class MidiPrinter {
+
+    public static final String[] CHANNEL_COMMAND_NAMES = { "NoteOff", "NoteOn",
+            "PolyTouch", "Control", "Program", "Pressure", "Bend" };
+    public static final String[] SYSTEM_COMMAND_NAMES = { "SysEx", // F0
+            "TimeCode",    // F1
+            "SongPos",     // F2
+            "SongSel",     // F3
+            "F4",          // F4
+            "F5",          // F5
+            "TuneReq",     // F6
+            "EndSysex",    // F7
+            "TimingClock", // F8
+            "F9",          // F9
+            "Start",       // FA
+            "Continue",    // FB
+            "Stop",        // FC
+            "FD",          // FD
+            "ActiveSensing", // FE
+            "Reset"        // FF
+    };
+
+    public static String getName(int status) {
+        if (status >= 0xF0) {
+            int index = status & 0x0F;
+            return SYSTEM_COMMAND_NAMES[index];
+        } else if (status >= 0x80) {
+            int index = (status >> 4) & 0x07;
+            return CHANNEL_COMMAND_NAMES[index];
+        } else {
+            return "data";
+        }
+    }
+
+    public static String formatBytes(byte[] data, int offset, int count) {
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < count; i++) {
+            sb.append(String.format(" %02X", data[offset + i]));
+        }
+        return sb.toString();
+    }
+
+    public static String formatMessage(byte[] data, int offset, int count) {
+        StringBuilder sb = new StringBuilder();
+        byte statusByte = data[offset++];
+        int status = statusByte & 0xFF;
+        sb.append(getName(status)).append("(");
+        int numData = MidiConstants.getBytesPerMessage(statusByte) - 1;
+        if ((status >= 0x80) && (status < 0xF0)) { // channel message
+            int channel = status & 0x0F;
+            // Add 1 for humans who think channels are numbered 1-16.
+            sb.append((channel + 1)).append(", ");
+        }
+        for (int i = 0; i < numData; i++) {
+            if (i > 0) {
+                sb.append(", ");
+            }
+            sb.append(data[offset++]);
+        }
+        sb.append(")");
+        return sb.toString();
+    }
+
+    public static String formatDeviceInfo(MidiDeviceInfo info) {
+        StringBuilder sb = new StringBuilder();
+        if (info != null) {
+            Bundle properties = info.getProperties();
+            for (String key : properties.keySet()) {
+                Object value = properties.get(key);
+                sb.append(key).append(" = ").append(value).append('\n');
+            }
+            for (PortInfo port : info.getPorts()) {
+                sb.append((port.getType() == PortInfo.TYPE_INPUT) ? "input"
+                        : "output");
+                sb.append("[").append(port.getPortNumber()).append("] = \"").append(port.getName()
+                        + "\"\n");
+            }
+        }
+        return sb.toString();
+    }
+}
diff --git a/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/midiscope/MidiScope.java b/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/midiscope/MidiScope.java
new file mode 100644
index 0000000..3965d83
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/midiscope/MidiScope.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2015 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.android.midiscope;
+
+import android.media.midi.MidiDeviceService;
+import android.media.midi.MidiDeviceStatus;
+import android.media.midi.MidiReceiver;
+
+import com.example.android.common.midi.MidiFramer;
+
+import java.io.IOException;
+
+/**
+ * Virtual MIDI Device that logs messages to a ScopeLogger.
+ */
+
+public class MidiScope extends MidiDeviceService {
+
+    private static ScopeLogger mScopeLogger;
+    private MidiReceiver mInputReceiver = new MyReceiver();
+    private static MidiFramer mDeviceFramer;
+
+    @Override
+    public MidiReceiver[] onGetInputPortReceivers() {
+        return new MidiReceiver[] { mInputReceiver };
+    }
+
+    public static ScopeLogger getScopeLogger() {
+        return mScopeLogger;
+    }
+
+    public static void setScopeLogger(ScopeLogger logger) {
+        if (logger != null) {
+            // Receiver that prints the messages.
+            LoggingReceiver loggingReceiver = new LoggingReceiver(logger);
+            mDeviceFramer = new MidiFramer(loggingReceiver);
+        }
+        mScopeLogger = logger;
+    }
+
+    private static class MyReceiver extends MidiReceiver {
+        @Override
+        public void onSend(byte[] data, int offset, int count,
+                long timestamp) throws IOException {
+            if (mScopeLogger != null) {
+                // Send raw data to be parsed into discrete messages.
+                mDeviceFramer.send(data, offset, count, timestamp);
+            }
+        }
+    }
+
+    /**
+     * This will get called when clients connect or disconnect.
+     * Log device information.
+     */
+    @Override
+    public void onDeviceStatusChanged(MidiDeviceStatus status) {
+        if (mScopeLogger != null) {
+            if (status.isInputPortOpen(0)) {
+                mScopeLogger.log("=== connected ===");
+                String text = MidiPrinter.formatDeviceInfo(
+                        status.getDeviceInfo());
+                mScopeLogger.log(text);
+            } else {
+                mScopeLogger.log("--- disconnected ---");
+            }
+        }
+    }
+}
diff --git a/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/midiscope/ScopeLogger.java b/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/midiscope/ScopeLogger.java
new file mode 100644
index 0000000..dc52efd
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/Application/src/main/java/com/example/android/midiscope/ScopeLogger.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2015 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.android.midiscope;
+
+public interface ScopeLogger {
+    /**
+     * Write the text string somewhere that the user can see it.
+     * @param text
+     */
+    void log(String text);
+}
diff --git a/prebuilts/gradle/MidiScope/Application/src/main/res/drawable-hdpi/ic_clear_all.png b/prebuilts/gradle/MidiScope/Application/src/main/res/drawable-hdpi/ic_clear_all.png
new file mode 100755
index 0000000..e23d886
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/Application/src/main/res/drawable-hdpi/ic_clear_all.png
Binary files differ
diff --git a/prebuilts/gradle/MidiScope/Application/src/main/res/drawable-hdpi/tile.9.png b/prebuilts/gradle/MidiScope/Application/src/main/res/drawable-hdpi/tile.9.png
new file mode 100644
index 0000000..1358628
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/Application/src/main/res/drawable-hdpi/tile.9.png
Binary files differ
diff --git a/prebuilts/gradle/MidiScope/Application/src/main/res/drawable-mdpi/ic_clear_all.png b/prebuilts/gradle/MidiScope/Application/src/main/res/drawable-mdpi/ic_clear_all.png
new file mode 100755
index 0000000..dca3048
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/Application/src/main/res/drawable-mdpi/ic_clear_all.png
Binary files differ
diff --git a/prebuilts/gradle/MidiScope/Application/src/main/res/drawable-xhdpi/ic_clear_all.png b/prebuilts/gradle/MidiScope/Application/src/main/res/drawable-xhdpi/ic_clear_all.png
new file mode 100755
index 0000000..fef5dcd
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/Application/src/main/res/drawable-xhdpi/ic_clear_all.png
Binary files differ
diff --git a/prebuilts/gradle/MidiScope/Application/src/main/res/drawable-xxhdpi/ic_clear_all.png b/prebuilts/gradle/MidiScope/Application/src/main/res/drawable-xxhdpi/ic_clear_all.png
new file mode 100755
index 0000000..51d2d3d
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/Application/src/main/res/drawable-xxhdpi/ic_clear_all.png
Binary files differ
diff --git a/prebuilts/gradle/MidiScope/Application/src/main/res/drawable-xxxhdpi/ic_clear_all.png b/prebuilts/gradle/MidiScope/Application/src/main/res/drawable-xxxhdpi/ic_clear_all.png
new file mode 100755
index 0000000..9dbccf3
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/Application/src/main/res/drawable-xxxhdpi/ic_clear_all.png
Binary files differ
diff --git a/prebuilts/gradle/MidiScope/Application/src/main/res/layout/main.xml b/prebuilts/gradle/MidiScope/Application/src/main/res/layout/main.xml
new file mode 100644
index 0000000..71c52aa
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/Application/src/main/res/layout/main.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 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_width="match_parent"
+              android:layout_height="match_parent"
+              android:orientation="vertical">
+
+    <Toolbar
+        android:id="@+id/toolbar"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:background="?android:attr/colorPrimary"
+        android:elevation="4dp"
+        android:minHeight="?android:attr/actionBarSize"
+        android:popupTheme="@android:style/ThemeOverlay.Material.Light"
+        android:theme="@android:style/ThemeOverlay.Material.Dark.ActionBar">
+
+        <Spinner
+            android:id="@+id/spinner_senders"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:entries="@array/senders"
+            android:popupTheme="@android:style/ThemeOverlay.Material.Light"/>
+
+    </Toolbar>
+
+    <ScrollView
+        android:id="@+id/scroll"
+        android:layout_width="match_parent"
+        android:layout_height="0px"
+        android:layout_weight="1">
+
+        <TextView
+            android:id="@+id/log"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingBottom="8dp"
+            android:paddingEnd="16dp"
+            android:paddingStart="16dp"
+            android:paddingTop="8dp"
+            android:textAppearance="?android:attr/textAppearanceMedium"/>
+
+    </ScrollView>
+
+</LinearLayout>
diff --git a/prebuilts/gradle/MidiScope/Application/src/main/res/menu/main.xml b/prebuilts/gradle/MidiScope/Application/src/main/res/menu/main.xml
new file mode 100644
index 0000000..abb1842
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/Application/src/main/res/menu/main.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2015 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.
+-->
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item
+        android:id="@+id/action_clear_all"
+        android:icon="@drawable/ic_clear_all"
+        android:showAsAction="ifRoom"
+        android:title="@string/clear_log"/>
+
+    <item
+        android:id="@+id/action_keep_screen_on"
+        android:checkable="true"
+        android:checked="true"
+        android:showAsAction="never"
+        android:title="@string/keep_screen_on"/>
+
+</menu>
diff --git a/prebuilts/gradle/MidiScope/Application/src/main/res/mipmap-hdpi/ic_launcher.png b/prebuilts/gradle/MidiScope/Application/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100755
index 0000000..4a75524
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/Application/src/main/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/prebuilts/gradle/MidiScope/Application/src/main/res/mipmap-mdpi/ic_launcher.png b/prebuilts/gradle/MidiScope/Application/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100755
index 0000000..09a4271
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/Application/src/main/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/prebuilts/gradle/MidiScope/Application/src/main/res/mipmap-xhdpi/ic_launcher.png b/prebuilts/gradle/MidiScope/Application/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100755
index 0000000..e9c9a36
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/Application/src/main/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/prebuilts/gradle/MidiScope/Application/src/main/res/mipmap-xxhdpi/ic_launcher.png b/prebuilts/gradle/MidiScope/Application/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100755
index 0000000..6e79c3b
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/Application/src/main/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/prebuilts/gradle/MidiScope/Application/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/prebuilts/gradle/MidiScope/Application/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100755
index 0000000..638d4e1
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/Application/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/prebuilts/gradle/MidiScope/Application/src/main/res/values-sw600dp/template-dimens.xml b/prebuilts/gradle/MidiScope/Application/src/main/res/values-sw600dp/template-dimens.xml
new file mode 100644
index 0000000..22074a2
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/Application/src/main/res/values-sw600dp/template-dimens.xml
@@ -0,0 +1,24 @@
+<!--
+  Copyright 2013 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>
+
+    <!-- Semantic definitions -->
+
+    <dimen name="horizontal_page_margin">@dimen/margin_huge</dimen>
+    <dimen name="vertical_page_margin">@dimen/margin_medium</dimen>
+
+</resources>
diff --git a/prebuilts/gradle/MidiScope/Application/src/main/res/values-sw600dp/template-styles.xml b/prebuilts/gradle/MidiScope/Application/src/main/res/values-sw600dp/template-styles.xml
new file mode 100644
index 0000000..03d1974
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/Application/src/main/res/values-sw600dp/template-styles.xml
@@ -0,0 +1,25 @@
+<!--
+  Copyright 2013 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="Widget.SampleMessage">
+        <item name="android:textAppearance">?android:textAppearanceLarge</item>
+        <item name="android:lineSpacingMultiplier">1.2</item>
+        <item name="android:shadowDy">-6.5</item>
+    </style>
+
+</resources>
diff --git a/prebuilts/gradle/MidiScope/Application/src/main/res/values-v11/template-styles.xml b/prebuilts/gradle/MidiScope/Application/src/main/res/values-v11/template-styles.xml
new file mode 100644
index 0000000..8c1ea66
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/Application/src/main/res/values-v11/template-styles.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright 2013 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>
+
+    <!-- Activity themes -->
+    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
+
+</resources>
diff --git a/prebuilts/gradle/MidiScope/Application/src/main/res/values-v21/base-colors.xml b/prebuilts/gradle/MidiScope/Application/src/main/res/values-v21/base-colors.xml
new file mode 100644
index 0000000..8b6ec3f
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/Application/src/main/res/values-v21/base-colors.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2013 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>
+
+
+</resources>
diff --git a/prebuilts/gradle/MidiScope/Application/src/main/res/values-v21/base-template-styles.xml b/prebuilts/gradle/MidiScope/Application/src/main/res/values-v21/base-template-styles.xml
new file mode 100644
index 0000000..c778e4f
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/Application/src/main/res/values-v21/base-template-styles.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2013 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>
+
+    <!-- Activity themes -->
+    <style name="Theme.Base" parent="android:Theme.Material.Light">
+    </style>
+
+</resources>
diff --git a/prebuilts/gradle/MidiScope/Application/src/main/res/values/base-strings.xml b/prebuilts/gradle/MidiScope/Application/src/main/res/values/base-strings.xml
new file mode 100644
index 0000000..fb7d652
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/Application/src/main/res/values/base-strings.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2013 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>
+    <string name="app_name">MidiScope</string>
+    <string name="intro_message">
+        <![CDATA[
+        
+            
+This sample demonstrates how to use the MIDI API to receive and process MIDI signals coming from an
+attached input device.
+            
+        
+        ]]>
+    </string>
+</resources>
diff --git a/prebuilts/gradle/MidiScope/Application/src/main/res/values/colors.xml b/prebuilts/gradle/MidiScope/Application/src/main/res/values/colors.xml
new file mode 100644
index 0000000..eef48d8
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/Application/src/main/res/values/colors.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2015 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="primary">#009688</color>
+    <color name="primary_dark">#00796B</color>
+    <color name="primary_light">#B2DFDB</color>
+    <color name="accent">#FFC107</color>
+    <color name="primary_text">#212121</color>
+    <color name="secondary_text">#727272</color>
+    <color name="icons">#FFFFFF</color>
+    <color name="divider">#B6B6B6</color>
+</resources>
diff --git a/prebuilts/gradle/MidiScope/Application/src/main/res/values/strings.xml b/prebuilts/gradle/MidiScope/Application/src/main/res/values/strings.xml
new file mode 100644
index 0000000..52beb4e
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/Application/src/main/res/values/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2015 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>
+    <string name="header_text">Select a MIDI source from the Spinner above or send messages to MidiScope.</string>
+    <string name="clear_log">Clear Log</string>
+    <string name="keep_screen_on">Keep Screen On</string>
+    <string-array name="senders">
+        <item>"none"</item>
+    </string-array>
+</resources>
diff --git a/prebuilts/gradle/MidiScope/Application/src/main/res/values/styles.xml b/prebuilts/gradle/MidiScope/Application/src/main/res/values/styles.xml
new file mode 100644
index 0000000..0036009
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/Application/src/main/res/values/styles.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2015 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="MidiScopeTheme" parent="android:Theme.Material.Light.NoActionBar">
+        <item name="android:colorPrimary">@color/primary</item>
+        <item name="android:colorPrimaryDark">@color/primary_dark</item>
+        <item name="android:colorAccent">@color/accent</item>
+    </style>
+
+</resources>
diff --git a/prebuilts/gradle/MidiScope/Application/src/main/res/values/template-dimens.xml b/prebuilts/gradle/MidiScope/Application/src/main/res/values/template-dimens.xml
new file mode 100644
index 0000000..39e710b
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/Application/src/main/res/values/template-dimens.xml
@@ -0,0 +1,32 @@
+<!--
+  Copyright 2013 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>
+
+    <!-- Define standard dimensions to comply with Holo-style grids and rhythm. -->
+
+    <dimen name="margin_tiny">4dp</dimen>
+    <dimen name="margin_small">8dp</dimen>
+    <dimen name="margin_medium">16dp</dimen>
+    <dimen name="margin_large">32dp</dimen>
+    <dimen name="margin_huge">64dp</dimen>
+
+    <!-- Semantic definitions -->
+
+    <dimen name="horizontal_page_margin">@dimen/margin_medium</dimen>
+    <dimen name="vertical_page_margin">@dimen/margin_medium</dimen>
+
+</resources>
diff --git a/prebuilts/gradle/MidiScope/Application/src/main/res/values/template-styles.xml b/prebuilts/gradle/MidiScope/Application/src/main/res/values/template-styles.xml
new file mode 100644
index 0000000..6e7d593
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/Application/src/main/res/values/template-styles.xml
@@ -0,0 +1,42 @@
+<!--
+  Copyright 2013 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>
+
+    <!-- Activity themes -->
+
+    <style name="Theme.Base" parent="android:Theme.Light" />
+
+    <style name="Theme.Sample" parent="Theme.Base" />
+
+    <style name="AppTheme" parent="Theme.Sample" />
+    <!-- Widget styling -->
+
+    <style name="Widget" />
+
+    <style name="Widget.SampleMessage">
+        <item name="android:textAppearance">?android:textAppearanceMedium</item>
+        <item name="android:lineSpacingMultiplier">1.1</item>
+    </style>
+
+    <style name="Widget.SampleMessageTile">
+        <item name="android:background">@drawable/tile</item>
+        <item name="android:shadowColor">#7F000000</item>
+        <item name="android:shadowDy">-3.5</item>
+        <item name="android:shadowRadius">2</item>
+    </style>
+
+</resources>
diff --git a/prebuilts/gradle/MidiScope/Application/src/main/res/xml/scope_device_info.xml b/prebuilts/gradle/MidiScope/Application/src/main/res/xml/scope_device_info.xml
new file mode 100644
index 0000000..f89f110
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/Application/src/main/res/xml/scope_device_info.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2015 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.
+-->
+<devices>
+    <device
+        name="AndroidMidiScope"
+        manufacturer="AndroidTest"
+        product="Scope">
+        <input-port name="input"/>
+    </device>
+</devices>
diff --git a/prebuilts/gradle/MidiScope/CONTRIBUTING.md b/prebuilts/gradle/MidiScope/CONTRIBUTING.md
new file mode 100644
index 0000000..faa8b5c
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/CONTRIBUTING.md
@@ -0,0 +1,35 @@
+# How to become a contributor and submit your own code
+
+## Contributor License Agreements
+
+We'd love to accept your sample apps and patches! Before we can take them, we
+have to jump a couple of legal hurdles.
+
+Please fill out either the individual or corporate Contributor License Agreement (CLA).
+
+  * If you are an individual writing original source code and you're sure you
+    own the intellectual property, then you'll need to sign an [individual CLA]
+    (https://cla.developers.google.com).
+  * If you work for a company that wants to allow you to contribute your work,
+    then you'll need to sign a [corporate CLA]
+    (https://cla.developers.google.com).
+
+Follow either of the two links above to access the appropriate CLA and
+instructions for how to sign and return it. Once we receive it, we'll be able to
+accept your pull requests.
+
+## Contributing A Patch
+
+1. Submit an issue describing your proposed change to the repo in question.
+1. The repo owner will respond to your issue promptly.
+1. If your proposed change is accepted, and you haven't already done so, sign a
+   Contributor License Agreement (see details above).
+1. Fork the desired repo, develop and test your code changes.
+1. Ensure that your code adheres to the existing style in the sample to which
+   you are contributing. Refer to the
+   [Android Code Style Guide]
+   (https://source.android.com/source/code-style.html) for the
+   recommended coding standards for this organization.
+1. Ensure that your code has an appropriate set of unit tests which all pass.
+1. Submit a pull request.
+
diff --git a/prebuilts/gradle/MidiScope/LICENSE b/prebuilts/gradle/MidiScope/LICENSE
new file mode 100644
index 0000000..4f22946
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/LICENSE
@@ -0,0 +1,647 @@
+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
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "{}"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright {yyyy} {name of copyright owner}
+
+   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.
+
+All image and audio files (including *.png, *.jpg, *.svg, *.mp3, *.wav 
+and *.ogg) are licensed under the CC-BY-NC license. All other files are 
+licensed under the Apache 2 license.
+
+CC-BY-NC License
+----------------
+
+Attribution-NonCommercial-ShareAlike 4.0 International
+
+=======================================================================
+
+Creative Commons Corporation ("Creative Commons") is not a law firm and
+does not provide legal services or legal advice. Distribution of
+Creative Commons public licenses does not create a lawyer-client or
+other relationship. Creative Commons makes its licenses and related
+information available on an "as-is" basis. Creative Commons gives no
+warranties regarding its licenses, any material licensed under their
+terms and conditions, or any related information. Creative Commons
+disclaims all liability for damages resulting from their use to the
+fullest extent possible.
+
+Using Creative Commons Public Licenses
+
+Creative Commons public licenses provide a standard set of terms and
+conditions that creators and other rights holders may use to share
+original works of authorship and other material subject to copyright
+and certain other rights specified in the public license below. The
+following considerations are for informational purposes only, are not
+exhaustive, and do not form part of our licenses.
+
+     Considerations for licensors: Our public licenses are
+     intended for use by those authorized to give the public
+     permission to use material in ways otherwise restricted by
+     copyright and certain other rights. Our licenses are
+     irrevocable. Licensors should read and understand the terms
+     and conditions of the license they choose before applying it.
+     Licensors should also secure all rights necessary before
+     applying our licenses so that the public can reuse the
+     material as expected. Licensors should clearly mark any
+     material not subject to the license. This includes other CC-
+     licensed material, or material used under an exception or
+     limitation to copyright. More considerations for licensors:
+	wiki.creativecommons.org/Considerations_for_licensors
+
+     Considerations for the public: By using one of our public
+     licenses, a licensor grants the public permission to use the
+     licensed material under specified terms and conditions. If
+     the licensor's permission is not necessary for any reason--for
+     example, because of any applicable exception or limitation to
+     copyright--then that use is not regulated by the license. Our
+     licenses grant only permissions under copyright and certain
+     other rights that a licensor has authority to grant. Use of
+     the licensed material may still be restricted for other
+     reasons, including because others have copyright or other
+     rights in the material. A licensor may make special requests,
+     such as asking that all changes be marked or described.
+     Although not required by our licenses, you are encouraged to
+     respect those requests where reasonable. More_considerations
+     for the public: 
+	wiki.creativecommons.org/Considerations_for_licensees
+
+=======================================================================
+
+Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International
+Public License
+
+By exercising the Licensed Rights (defined below), You accept and agree
+to be bound by the terms and conditions of this Creative Commons
+Attribution-NonCommercial-ShareAlike 4.0 International Public License
+("Public License"). To the extent this Public License may be
+interpreted as a contract, You are granted the Licensed Rights in
+consideration of Your acceptance of these terms and conditions, and the
+Licensor grants You such rights in consideration of benefits the
+Licensor receives from making the Licensed Material available under
+these terms and conditions.
+
+
+Section 1 -- Definitions.
+
+  a. Adapted Material means material subject to Copyright and Similar
+     Rights that is derived from or based upon the Licensed Material
+     and in which the Licensed Material is translated, altered,
+     arranged, transformed, or otherwise modified in a manner requiring
+     permission under the Copyright and Similar Rights held by the
+     Licensor. For purposes of this Public License, where the Licensed
+     Material is a musical work, performance, or sound recording,
+     Adapted Material is always produced where the Licensed Material is
+     synched in timed relation with a moving image.
+
+  b. Adapter's License means the license You apply to Your Copyright
+     and Similar Rights in Your contributions to Adapted Material in
+     accordance with the terms and conditions of this Public License.
+
+  c. BY-NC-SA Compatible License means a license listed at
+     creativecommons.org/compatiblelicenses, approved by Creative
+     Commons as essentially the equivalent of this Public License.
+
+  d. Copyright and Similar Rights means copyright and/or similar rights
+     closely related to copyright including, without limitation,
+     performance, broadcast, sound recording, and Sui Generis Database
+     Rights, without regard to how the rights are labeled or
+     categorized. For purposes of this Public License, the rights
+     specified in Section 2(b)(1)-(2) are not Copyright and Similar
+     Rights.
+
+  e. Effective Technological Measures means those measures that, in the
+     absence of proper authority, may not be circumvented under laws
+     fulfilling obligations under Article 11 of the WIPO Copyright
+     Treaty adopted on December 20, 1996, and/or similar international
+     agreements.
+
+  f. Exceptions and Limitations means fair use, fair dealing, and/or
+     any other exception or limitation to Copyright and Similar Rights
+     that applies to Your use of the Licensed Material.
+
+  g. License Elements means the license attributes listed in the name
+     of a Creative Commons Public License. The License Elements of this
+     Public License are Attribution, NonCommercial, and ShareAlike.
+
+  h. Licensed Material means the artistic or literary work, database,
+     or other material to which the Licensor applied this Public
+     License.
+
+  i. Licensed Rights means the rights granted to You subject to the
+     terms and conditions of this Public License, which are limited to
+     all Copyright and Similar Rights that apply to Your use of the
+     Licensed Material and that the Licensor has authority to license.
+
+  j. Licensor means the individual(s) or entity(ies) granting rights
+     under this Public License.
+
+  k. NonCommercial means not primarily intended for or directed towards
+     commercial advantage or monetary compensation. For purposes of
+     this Public License, the exchange of the Licensed Material for
+     other material subject to Copyright and Similar Rights by digital
+     file-sharing or similar means is NonCommercial provided there is
+     no payment of monetary compensation in connection with the
+     exchange.
+
+  l. Share means to provide material to the public by any means or
+     process that requires permission under the Licensed Rights, such
+     as reproduction, public display, public performance, distribution,
+     dissemination, communication, or importation, and to make material
+     available to the public including in ways that members of the
+     public may access the material from a place and at a time
+     individually chosen by them.
+
+  m. Sui Generis Database Rights means rights other than copyright
+     resulting from Directive 96/9/EC of the European Parliament and of
+     the Council of 11 March 1996 on the legal protection of databases,
+     as amended and/or succeeded, as well as other essentially
+     equivalent rights anywhere in the world.
+
+  n. You means the individual or entity exercising the Licensed Rights
+     under this Public License. Your has a corresponding meaning.
+
+
+Section 2 -- Scope.
+
+  a. License grant.
+
+       1. Subject to the terms and conditions of this Public License,
+          the Licensor hereby grants You a worldwide, royalty-free,
+          non-sublicensable, non-exclusive, irrevocable license to
+          exercise the Licensed Rights in the Licensed Material to:
+
+            a. reproduce and Share the Licensed Material, in whole or
+               in part, for NonCommercial purposes only; and
+
+            b. produce, reproduce, and Share Adapted Material for
+               NonCommercial purposes only.
+
+       2. Exceptions and Limitations. For the avoidance of doubt, where
+          Exceptions and Limitations apply to Your use, this Public
+          License does not apply, and You do not need to comply with
+          its terms and conditions.
+
+       3. Term. The term of this Public License is specified in Section
+          6(a).
+
+       4. Media and formats; technical modifications allowed. The
+          Licensor authorizes You to exercise the Licensed Rights in
+          all media and formats whether now known or hereafter created,
+          and to make technical modifications necessary to do so. The
+          Licensor waives and/or agrees not to assert any right or
+          authority to forbid You from making technical modifications
+          necessary to exercise the Licensed Rights, including
+          technical modifications necessary to circumvent Effective
+          Technological Measures. For purposes of this Public License,
+          simply making modifications authorized by this Section 2(a)
+          (4) never produces Adapted Material.
+
+       5. Downstream recipients.
+
+            a. Offer from the Licensor -- Licensed Material. Every
+               recipient of the Licensed Material automatically
+               receives an offer from the Licensor to exercise the
+               Licensed Rights under the terms and conditions of this
+               Public License.
+
+            b. Additional offer from the Licensor -- Adapted Material.
+               Every recipient of Adapted Material from You
+               automatically receives an offer from the Licensor to
+               exercise the Licensed Rights in the Adapted Material
+               under the conditions of the Adapter's License You apply.
+
+            c. No downstream restrictions. You may not offer or impose
+               any additional or different terms or conditions on, or
+               apply any Effective Technological Measures to, the
+               Licensed Material if doing so restricts exercise of the
+               Licensed Rights by any recipient of the Licensed
+               Material.
+
+       6. No endorsement. Nothing in this Public License constitutes or
+          may be construed as permission to assert or imply that You
+          are, or that Your use of the Licensed Material is, connected
+          with, or sponsored, endorsed, or granted official status by,
+          the Licensor or others designated to receive attribution as
+          provided in Section 3(a)(1)(A)(i).
+
+  b. Other rights.
+
+       1. Moral rights, such as the right of integrity, are not
+          licensed under this Public License, nor are publicity,
+          privacy, and/or other similar personality rights; however, to
+          the extent possible, the Licensor waives and/or agrees not to
+          assert any such rights held by the Licensor to the limited
+          extent necessary to allow You to exercise the Licensed
+          Rights, but not otherwise.
+
+       2. Patent and trademark rights are not licensed under this
+          Public License.
+
+       3. To the extent possible, the Licensor waives any right to
+          collect royalties from You for the exercise of the Licensed
+          Rights, whether directly or through a collecting society
+          under any voluntary or waivable statutory or compulsory
+          licensing scheme. In all other cases the Licensor expressly
+          reserves any right to collect such royalties, including when
+          the Licensed Material is used other than for NonCommercial
+          purposes.
+
+
+Section 3 -- License Conditions.
+
+Your exercise of the Licensed Rights is expressly made subject to the
+following conditions.
+
+  a. Attribution.
+
+       1. If You Share the Licensed Material (including in modified
+          form), You must:
+
+            a. retain the following if it is supplied by the Licensor
+               with the Licensed Material:
+
+                 i. identification of the creator(s) of the Licensed
+                    Material and any others designated to receive
+                    attribution, in any reasonable manner requested by
+                    the Licensor (including by pseudonym if
+                    designated);
+
+                ii. a copyright notice;
+
+               iii. a notice that refers to this Public License;
+
+                iv. a notice that refers to the disclaimer of
+                    warranties;
+
+                 v. a URI or hyperlink to the Licensed Material to the
+                    extent reasonably practicable;
+
+            b. indicate if You modified the Licensed Material and
+               retain an indication of any previous modifications; and
+
+            c. indicate the Licensed Material is licensed under this
+               Public License, and include the text of, or the URI or
+               hyperlink to, this Public License.
+
+       2. You may satisfy the conditions in Section 3(a)(1) in any
+          reasonable manner based on the medium, means, and context in
+          which You Share the Licensed Material. For example, it may be
+          reasonable to satisfy the conditions by providing a URI or
+          hyperlink to a resource that includes the required
+          information.
+       3. If requested by the Licensor, You must remove any of the
+          information required by Section 3(a)(1)(A) to the extent
+          reasonably practicable.
+
+  b. ShareAlike.
+
+     In addition to the conditions in Section 3(a), if You Share
+     Adapted Material You produce, the following conditions also apply.
+
+       1. The Adapter's License You apply must be a Creative Commons
+          license with the same License Elements, this version or
+          later, or a BY-NC-SA Compatible License.
+
+       2. You must include the text of, or the URI or hyperlink to, the
+          Adapter's License You apply. You may satisfy this condition
+          in any reasonable manner based on the medium, means, and
+          context in which You Share Adapted Material.
+
+       3. You may not offer or impose any additional or different terms
+          or conditions on, or apply any Effective Technological
+          Measures to, Adapted Material that restrict exercise of the
+          rights granted under the Adapter's License You apply.
+
+
+Section 4 -- Sui Generis Database Rights.
+
+Where the Licensed Rights include Sui Generis Database Rights that
+apply to Your use of the Licensed Material:
+
+  a. for the avoidance of doubt, Section 2(a)(1) grants You the right
+     to extract, reuse, reproduce, and Share all or a substantial
+     portion of the contents of the database for NonCommercial purposes
+     only;
+
+  b. if You include all or a substantial portion of the database
+     contents in a database in which You have Sui Generis Database
+     Rights, then the database in which You have Sui Generis Database
+     Rights (but not its individual contents) is Adapted Material,
+     including for purposes of Section 3(b); and
+
+  c. You must comply with the conditions in Section 3(a) if You Share
+     all or a substantial portion of the contents of the database.
+
+For the avoidance of doubt, this Section 4 supplements and does not
+replace Your obligations under this Public License where the Licensed
+Rights include other Copyright and Similar Rights.
+
+
+Section 5 -- Disclaimer of Warranties and Limitation of Liability.
+
+  a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
+     EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
+     AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
+     ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
+     IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
+     WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
+     PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
+     ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
+     KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
+     ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
+
+  b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
+     TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
+     NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
+     INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
+     COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
+     USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
+     ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
+     DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
+     IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
+
+  c. The disclaimer of warranties and limitation of liability provided
+     above shall be interpreted in a manner that, to the extent
+     possible, most closely approximates an absolute disclaimer and
+     waiver of all liability.
+
+
+Section 6 -- Term and Termination.
+
+  a. This Public License applies for the term of the Copyright and
+     Similar Rights licensed here. However, if You fail to comply with
+     this Public License, then Your rights under this Public License
+     terminate automatically.
+
+  b. Where Your right to use the Licensed Material has terminated under
+     Section 6(a), it reinstates:
+
+       1. automatically as of the date the violation is cured, provided
+          it is cured within 30 days of Your discovery of the
+          violation; or
+
+       2. upon express reinstatement by the Licensor.
+
+     For the avoidance of doubt, this Section 6(b) does not affect any
+     right the Licensor may have to seek remedies for Your violations
+     of this Public License.
+
+  c. For the avoidance of doubt, the Licensor may also offer the
+     Licensed Material under separate terms or conditions or stop
+     distributing the Licensed Material at any time; however, doing so
+     will not terminate this Public License.
+
+  d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
+     License.
+
+
+Section 7 -- Other Terms and Conditions.
+
+  a. The Licensor shall not be bound by any additional or different
+     terms or conditions communicated by You unless expressly agreed.
+
+  b. Any arrangements, understandings, or agreements regarding the
+     Licensed Material not stated herein are separate from and
+     independent of the terms and conditions of this Public License.
+
+
+Section 8 -- Interpretation.
+
+  a. For the avoidance of doubt, this Public License does not, and
+     shall not be interpreted to, reduce, limit, restrict, or impose
+     conditions on any use of the Licensed Material that could lawfully
+     be made without permission under this Public License.
+
+  b. To the extent possible, if any provision of this Public License is
+     deemed unenforceable, it shall be automatically reformed to the
+     minimum extent necessary to make it enforceable. If the provision
+     cannot be reformed, it shall be severed from this Public License
+     without affecting the enforceability of the remaining terms and
+     conditions.
+
+  c. No term or condition of this Public License will be waived and no
+     failure to comply consented to unless expressly agreed to by the
+     Licensor.
+
+  d. Nothing in this Public License constitutes or may be interpreted
+     as a limitation upon, or waiver of, any privileges and immunities
+     that apply to the Licensor or You, including from the legal
+     processes of any jurisdiction or authority.
+
+=======================================================================
+
+Creative Commons is not a party to its public licenses.
+Notwithstanding, Creative Commons may elect to apply one of its public
+licenses to material it publishes and in those instances will be
+considered the "Licensor." Except for the limited purpose of indicating
+that material is shared under a Creative Commons public license or as
+otherwise permitted by the Creative Commons policies published at
+creativecommons.org/policies, Creative Commons does not authorize the
+use of the trademark "Creative Commons" or any other trademark or logo
+of Creative Commons without its prior written consent including,
+without limitation, in connection with any unauthorized modifications
+to any of its public licenses or any other arrangements,
+understandings, or agreements concerning use of licensed material. For
+the avoidance of doubt, this paragraph does not form part of the public
+licenses.
+
+Creative Commons may be contacted at creativecommons.org.
+
diff --git a/prebuilts/gradle/MidiScope/README.md b/prebuilts/gradle/MidiScope/README.md
new file mode 100644
index 0000000..9291920
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/README.md
@@ -0,0 +1,67 @@
+
+Android MidiScope Sample
+===================================
+
+Sample demonstrating how to use the MIDI API to receive and process MIDI signals coming from an
+attached device.
+
+Introduction
+------------
+
+The Android MIDI API ([android.media.midi][1]) allows developers to connect a MIDI device to Android
+and process MIDI signals coming from it. This sample demonstrates some basic features of the MIDI
+API, such as enumeration of currently available devices (Information includes name, vendor,
+capabilities, etc), notification when MIDI devices are plugged in or unplugged, and receiving MIDI
+signals. This sample simply shows all the received MIDI signals to the screen log and does not play
+any sound for them.
+[1]: https://developer.android.com/reference/android/media/midi/package-summary.html
+
+Pre-requisites
+--------------
+
+- Android SDK v23
+- Android Build Tools v23.0.0
+- Android Support Repository
+
+Screenshots
+-------------
+
+<img src="screenshots/1-main.png" height="400" alt="Screenshot"/> <img src="screenshots/2-settings.png" height="400" alt="Screenshot"/> 
+
+Getting Started
+---------------
+
+This sample uses the Gradle build system. To build this project, use the
+"gradlew build" command or use "Import Project" in Android Studio.
+
+Support
+-------
+
+- Google+ Community: https://plus.google.com/communities/105153134372062985968
+- Stack Overflow: http://stackoverflow.com/questions/tagged/android
+
+If you've found an error in this sample, please file an issue:
+https://github.com/googlesamples/android-MidiScope
+
+Patches are encouraged, and may be submitted by forking this project and
+submitting a pull request through GitHub. Please see CONTRIBUTING.md for more details.
+
+License
+-------
+
+Copyright 2014 The Android Open Source Project, Inc.
+
+Licensed to the Apache Software Foundation (ASF) under one or more contributor
+license agreements.  See the NOTICE file distributed with this work for
+additional information regarding copyright ownership.  The ASF licenses this
+file to you 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.
diff --git a/prebuilts/gradle/MidiScope/build.gradle b/prebuilts/gradle/MidiScope/build.gradle
new file mode 100644
index 0000000..1901ba9
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/build.gradle
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/prebuilts/gradle/MidiScope/gradle/wrapper/gradle-wrapper.jar b/prebuilts/gradle/MidiScope/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..8c0fb64
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/prebuilts/gradle/MidiScope/gradle/wrapper/gradle-wrapper.properties b/prebuilts/gradle/MidiScope/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..afb3296
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Wed Apr 10 15:27:10 PDT 2013
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=http\://services.gradle.org/distributions/gradle-2.2.1-bin.zip
diff --git a/prebuilts/gradle/MidiScope/gradlew b/prebuilts/gradle/MidiScope/gradlew
new file mode 100755
index 0000000..91a7e26
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+    echo "$*"
+}
+
+die ( ) {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+    [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=$((i+1))
+    done
+    case $i in
+        (0) set -- ;;
+        (1) set -- "$args0" ;;
+        (2) set -- "$args0" "$args1" ;;
+        (3) set -- "$args0" "$args1" "$args2" ;;
+        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+    JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/prebuilts/gradle/MidiScope/gradlew.bat b/prebuilts/gradle/MidiScope/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off

+@rem ##########################################################################

+@rem

+@rem  Gradle startup script for Windows

+@rem

+@rem ##########################################################################

+

+@rem Set local scope for the variables with windows NT shell

+if "%OS%"=="Windows_NT" setlocal

+

+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.

+set DEFAULT_JVM_OPTS=

+

+set DIRNAME=%~dp0

+if "%DIRNAME%" == "" set DIRNAME=.

+set APP_BASE_NAME=%~n0

+set APP_HOME=%DIRNAME%

+

+@rem Find java.exe

+if defined JAVA_HOME goto findJavaFromJavaHome

+

+set JAVA_EXE=java.exe

+%JAVA_EXE% -version >NUL 2>&1

+if "%ERRORLEVEL%" == "0" goto init

+

+echo.

+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.

+echo.

+echo Please set the JAVA_HOME variable in your environment to match the

+echo location of your Java installation.

+

+goto fail

+

+:findJavaFromJavaHome

+set JAVA_HOME=%JAVA_HOME:"=%

+set JAVA_EXE=%JAVA_HOME%/bin/java.exe

+

+if exist "%JAVA_EXE%" goto init

+

+echo.

+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%

+echo.

+echo Please set the JAVA_HOME variable in your environment to match the

+echo location of your Java installation.

+

+goto fail

+

+:init

+@rem Get command-line arguments, handling Windowz variants

+

+if not "%OS%" == "Windows_NT" goto win9xME_args

+if "%@eval[2+2]" == "4" goto 4NT_args

+

+:win9xME_args

+@rem Slurp the command line arguments.

+set CMD_LINE_ARGS=

+set _SKIP=2

+

+:win9xME_args_slurp

+if "x%~1" == "x" goto execute

+

+set CMD_LINE_ARGS=%*

+goto execute

+

+:4NT_args

+@rem Get arguments from the 4NT Shell from JP Software

+set CMD_LINE_ARGS=%$

+

+:execute

+@rem Setup the command line

+

+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar

+

+@rem Execute Gradle

+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%

+

+:end

+@rem End local scope for the variables with windows NT shell

+if "%ERRORLEVEL%"=="0" goto mainEnd

+

+:fail

+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of

+rem the _cmd.exe /c_ return code!

+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1

+exit /b 1

+

+:mainEnd

+if "%OS%"=="Windows_NT" endlocal

+

+:omega

diff --git a/prebuilts/gradle/MidiScope/screenshots/1-main.png b/prebuilts/gradle/MidiScope/screenshots/1-main.png
new file mode 100644
index 0000000..0b4fb8e
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/screenshots/1-main.png
Binary files differ
diff --git a/prebuilts/gradle/MidiScope/screenshots/2-signals.png b/prebuilts/gradle/MidiScope/screenshots/2-signals.png
new file mode 100644
index 0000000..7e4b443
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/screenshots/2-signals.png
Binary files differ
diff --git a/prebuilts/gradle/MidiScope/screenshots/icon-web.png b/prebuilts/gradle/MidiScope/screenshots/icon-web.png
new file mode 100644
index 0000000..6daec5c
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/screenshots/icon-web.png
Binary files differ
diff --git a/prebuilts/gradle/MidiScope/settings.gradle b/prebuilts/gradle/MidiScope/settings.gradle
new file mode 100644
index 0000000..0a5c310
--- /dev/null
+++ b/prebuilts/gradle/MidiScope/settings.gradle
@@ -0,0 +1,2 @@
+
+include 'Application'
diff --git a/prebuilts/gradle/MidiSynth/.google/packaging.yaml b/prebuilts/gradle/MidiSynth/.google/packaging.yaml
new file mode 100644
index 0000000..baa258c
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/.google/packaging.yaml
@@ -0,0 +1,18 @@
+
+# GOOGLE SAMPLE PACKAGING DATA
+#
+# This file is used by Google as part of our samples packaging process.
+# End users may safely ignore this file. It has no relevance to other systems.
+---
+status:       PUBLISHED
+technologies: [Android]
+categories:   [Media]
+languages:    [Java]
+solutions:    [Mobile]
+github:       android-MidiSynth
+level:        EXPERT
+icon:         screenshots/icon-web.png
+apiRefs:
+    - android:android.media.midi.MidiManager
+    - android:android.media.midi.MidiReceiver
+license: apache2
diff --git a/prebuilts/gradle/MidiSynth/Application/build.gradle b/prebuilts/gradle/MidiSynth/Application/build.gradle
new file mode 100644
index 0000000..4e8f709
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/Application/build.gradle
@@ -0,0 +1,73 @@
+
+buildscript {
+    repositories {
+        jcenter()
+    }
+
+    dependencies {
+        classpath 'com.android.tools.build:gradle:1.2.3'
+    }
+}
+
+apply plugin: 'com.android.application'
+
+repositories {
+    jcenter()
+}
+
+dependencies {
+    compile "com.android.support:support-v4:23.0.0"
+    compile "com.android.support:support-v13:23.0.0"
+    compile "com.android.support:cardview-v7:23.0.0"
+}
+
+// The sample build uses multiple directories to
+// keep boilerplate and common code separate from
+// the main sample code.
+List<String> dirs = [
+    'main',     // main sample code; look here for the interesting stuff.
+    'common',   // components that are reused by multiple samples
+    'template'] // boilerplate code that is generated by the sample template process
+
+android {
+    compileSdkVersion 23
+    buildToolsVersion "23.0.0"
+
+    defaultConfig {
+        minSdkVersion 23
+        targetSdkVersion 23
+    }
+
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_7
+        targetCompatibility JavaVersion.VERSION_1_7
+    }
+
+    sourceSets {
+        main {
+            dirs.each { dir ->
+                java.srcDirs "src/${dir}/java"
+                res.srcDirs "src/${dir}/res"
+            }
+        }
+        androidTest.setRoot('tests')
+        androidTest.java.srcDirs = ['tests/src']
+
+    }
+
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/prebuilts/gradle/MidiSynth/Application/src/main/AndroidManifest.xml b/prebuilts/gradle/MidiSynth/Application/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..1002419
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/Application/src/main/AndroidManifest.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2015 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 package="com.example.android.midisynth"
+          xmlns:android="http://schemas.android.com/apk/res/android"
+          android:versionCode="1"
+          android:versionName="1.0">
+
+    <uses-feature
+        android:name="android.software.midi"
+        android:required="true"/>
+
+    <application
+        android:allowBackup="true"
+        android:icon="@mipmap/ic_launcher"
+        android:label="@string/app_name"
+        android:theme="@style/MidiSynthTheme">
+
+        <activity
+            android:name=".MainActivity"
+            android:label="@string/app_name">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+
+        <service
+            android:name=".MidiSynthDeviceService"
+            android:permission="android.permission.BIND_MIDI_DEVICE_SERVICE">
+            <intent-filter>
+                <action android:name="android.media.midi.MidiDeviceService"/>
+            </intent-filter>
+            <meta-data
+                android:name="android.media.midi.MidiDeviceService"
+                android:resource="@xml/synth_device_info"/>
+        </service>
+
+    </application>
+
+</manifest>
diff --git a/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/EventScheduler.java b/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/EventScheduler.java
new file mode 100644
index 0000000..37c0140
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/EventScheduler.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2015 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.android.common.midi;
+
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+/**
+ * Store SchedulableEvents in a timestamped buffer.
+ * Events may be written in any order.
+ * Events will be read in sorted order.
+ * Events with the same timestamp will be read in the order they were added.
+ * 
+ * Only one Thread can write into the buffer.
+ * And only one Thread can read from the buffer.
+ */
+public class EventScheduler {
+    private static final long NANOS_PER_MILLI = 1000000;
+
+    private final Object lock = new Object();
+    private SortedMap<Long, FastEventQueue> mEventBuffer;
+    // This does not have to be guarded. It is only set by the writing thread.
+    // If the reader sees a null right before being set then that is OK.
+    private FastEventQueue mEventPool = null;
+    private static final int MAX_POOL_SIZE = 200;
+
+    public EventScheduler() {
+        mEventBuffer = new TreeMap<Long, FastEventQueue>();
+    }
+
+    // If we keep at least one node in the list then it can be atomic
+    // and non-blocking.
+    private class FastEventQueue {
+        // One thread takes from the beginning of the list.
+        volatile SchedulableEvent mFirst;
+        // A second thread returns events to the end of the list.
+        volatile SchedulableEvent mLast;
+        volatile long mEventsAdded;
+        volatile long mEventsRemoved;
+
+        FastEventQueue(SchedulableEvent event) {
+            mFirst = event;
+            mLast = mFirst;
+            mEventsAdded = 1; // Always created with one event added. Never empty.
+            mEventsRemoved = 0; // None removed yet.
+        }
+
+        int size() {
+            return (int)(mEventsAdded - mEventsRemoved);
+        }
+
+        /**
+         * Do not call this unless there is more than one event
+         * in the list.
+         * @return first event in the list
+         */
+        public SchedulableEvent remove() {
+            // Take first event.
+            mEventsRemoved++;
+            SchedulableEvent event = mFirst;
+            mFirst = event.mNext;
+            return event;
+        }
+
+        /**
+         * @param event
+         */
+        public void add(SchedulableEvent event) {
+            event.mNext = null;
+            mLast.mNext = event;
+            mLast = event;
+            mEventsAdded++;
+        }
+    }
+
+    /**
+     * Base class for events that can be stored in the EventScheduler.
+     */
+    public static class SchedulableEvent {
+        private long mTimestamp;
+        private SchedulableEvent mNext = null;
+
+        /**
+         * @param timestamp
+         */
+        public SchedulableEvent(long timestamp) {
+            mTimestamp = timestamp;
+        }
+
+        /**
+         * @return timestamp
+         */
+        public long getTimestamp() {
+            return mTimestamp;
+        }
+
+        /**
+         * The timestamp should not be modified when the event is in the
+         * scheduling buffer.
+         */
+        public void setTimestamp(long timestamp) {
+            mTimestamp = timestamp;
+        }
+    }
+
+    /**
+     * Get an event from the pool.
+     * Always leave at least one event in the pool.
+     * @return event or null
+     */
+    public SchedulableEvent removeEventfromPool() {
+        SchedulableEvent event = null;
+        if (mEventPool != null && (mEventPool.size() > 1)) {
+            event = mEventPool.remove();
+        }
+        return event;
+    }
+
+    /**
+     * Return events to a pool so they can be reused.
+     *
+     * @param event
+     */
+    public void addEventToPool(SchedulableEvent event) {
+        if (mEventPool == null) {
+            mEventPool = new FastEventQueue(event);
+        // If we already have enough items in the pool then just
+        // drop the event. This prevents unbounded memory leaks.
+        } else if (mEventPool.size() < MAX_POOL_SIZE) {
+            mEventPool.add(event);
+        }
+    }
+
+    /**
+     * Add an event to the scheduler. Events with the same time will be
+     * processed in order.
+     *
+     * @param event
+     */
+    public void add(SchedulableEvent event) {
+        synchronized (lock) {
+            FastEventQueue list = mEventBuffer.get(event.getTimestamp());
+            if (list == null) {
+                long lowestTime = mEventBuffer.isEmpty() ? Long.MAX_VALUE
+                        : mEventBuffer.firstKey();
+                list = new FastEventQueue(event);
+                mEventBuffer.put(event.getTimestamp(), list);
+                // If the event we added is earlier than the previous earliest
+                // event then notify any threads waiting for the next event.
+                if (event.getTimestamp() < lowestTime) {
+                    lock.notify();
+                }
+            } else {
+                list.add(event);
+            }
+        }
+    }
+
+    // Caller must synchronize on lock before calling.
+    private SchedulableEvent removeNextEventLocked(long lowestTime) {
+        SchedulableEvent event;
+        FastEventQueue list = mEventBuffer.get(lowestTime);
+        // Remove list from tree if this is the last node.
+        if ((list.size() == 1)) {
+            mEventBuffer.remove(lowestTime);
+        }
+        event = list.remove();
+        return event;
+    }
+
+    /**
+     * Check to see if any scheduled events are ready to be processed.
+     *
+     * @param timestamp
+     * @return next event or null if none ready
+     */
+    public SchedulableEvent getNextEvent(long time) {
+        SchedulableEvent event = null;
+        synchronized (lock) {
+            if (!mEventBuffer.isEmpty()) {
+                long lowestTime = mEventBuffer.firstKey();
+                // Is it time for this list to be processed?
+                if (lowestTime <= time) {
+                    event = removeNextEventLocked(lowestTime);
+                }
+            }
+        }
+        // Log.i(TAG, "getNextEvent: event = " + event);
+        return event;
+    }
+
+    /**
+     * Return the next available event or wait until there is an event ready to
+     * be processed. This method assumes that the timestamps are in nanoseconds
+     * and that the current time is System.nanoTime().
+     *
+     * @return event
+     * @throws InterruptedException
+     */
+    public SchedulableEvent waitNextEvent() throws InterruptedException {
+        SchedulableEvent event = null;
+        while (true) {
+            long millisToWait = Integer.MAX_VALUE;
+            synchronized (lock) {
+                if (!mEventBuffer.isEmpty()) {
+                    long now = System.nanoTime();
+                    long lowestTime = mEventBuffer.firstKey();
+                    // Is it time for the earliest list to be processed?
+                    if (lowestTime <= now) {
+                        event = removeNextEventLocked(lowestTime);
+                        break;
+                    } else {
+                        // Figure out how long to sleep until next event.
+                        long nanosToWait = lowestTime - now;
+                        // Add 1 millisecond so we don't wake up before it is
+                        // ready.
+                        millisToWait = 1 + (nanosToWait / NANOS_PER_MILLI);
+                        // Clip 64-bit value to 32-bit max.
+                        if (millisToWait > Integer.MAX_VALUE) {
+                            millisToWait = Integer.MAX_VALUE;
+                        }
+                    }
+                }
+                lock.wait((int) millisToWait);
+            }
+        }
+        return event;
+    }
+}
diff --git a/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/MidiConstants.java b/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/MidiConstants.java
new file mode 100644
index 0000000..38c25d5
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/MidiConstants.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2015 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.android.common.midi;
+
+/**
+ * MIDI related constants and static methods.
+ * These values are defined in the MIDI Standard 1.0
+ * available from the MIDI Manufacturers Association.
+ */
+public class MidiConstants {
+    protected final static String TAG = "MidiTools";
+    public static final byte STATUS_COMMAND_MASK = (byte) 0xF0;
+    public static final byte STATUS_CHANNEL_MASK = (byte) 0x0F;
+
+    // Channel voice messages.
+    public static final byte STATUS_NOTE_OFF = (byte) 0x80;
+    public static final byte STATUS_NOTE_ON = (byte) 0x90;
+    public static final byte STATUS_POLYPHONIC_AFTERTOUCH = (byte) 0xA0;
+    public static final byte STATUS_CONTROL_CHANGE = (byte) 0xB0;
+    public static final byte STATUS_PROGRAM_CHANGE = (byte) 0xC0;
+    public static final byte STATUS_CHANNEL_PRESSURE = (byte) 0xD0;
+    public static final byte STATUS_PITCH_BEND = (byte) 0xE0;
+
+    // System Common Messages.
+    public static final byte STATUS_SYSTEM_EXCLUSIVE = (byte) 0xF0;
+    public static final byte STATUS_MIDI_TIME_CODE = (byte) 0xF1;
+    public static final byte STATUS_SONG_POSITION = (byte) 0xF2;
+    public static final byte STATUS_SONG_SELECT = (byte) 0xF3;
+    public static final byte STATUS_TUNE_REQUEST = (byte) 0xF6;
+    public static final byte STATUS_END_SYSEX = (byte) 0xF7;
+
+    // System Real-Time Messages
+    public static final byte STATUS_TIMING_CLOCK = (byte) 0xF8;
+    public static final byte STATUS_START = (byte) 0xFA;
+    public static final byte STATUS_CONTINUE = (byte) 0xFB;
+    public static final byte STATUS_STOP = (byte) 0xFC;
+    public static final byte STATUS_ACTIVE_SENSING = (byte) 0xFE;
+    public static final byte STATUS_RESET = (byte) 0xFF;
+
+    /** Number of bytes in a message nc from 8c to Ec */
+    public final static int CHANNEL_BYTE_LENGTHS[] = { 3, 3, 3, 3, 2, 2, 3 };
+
+    /** Number of bytes in a message Fn from F0 to FF */
+    public final static int SYSTEM_BYTE_LENGTHS[] = { 1, 2, 3, 2, 1, 1, 1, 1, 1,
+            1, 1, 1, 1, 1, 1, 1 };
+
+    /**
+     * MIDI messages, except for SysEx, are 1,2 or 3 bytes long.
+     * You can tell how long a MIDI message is from the first status byte.
+     * Do not call this for SysEx, which has variable length.
+     * @param statusByte
+     * @return number of bytes in a complete message, zero if data byte passed
+     */
+    public static int getBytesPerMessage(byte statusByte) {
+        // Java bytes are signed so we need to mask off the high bits
+        // to get a value between 0 and 255.
+        int statusInt = statusByte & 0xFF;
+        if (statusInt >= 0xF0) {
+            // System messages use low nibble for size.
+            return SYSTEM_BYTE_LENGTHS[statusInt & 0x0F];
+        } else if(statusInt >= 0x80) {
+            // Channel voice messages use high nibble for size.
+            return CHANNEL_BYTE_LENGTHS[(statusInt >> 4) - 8];
+        } else {
+            return 0; // data byte
+        }
+    }
+
+    /**
+     * @param msg
+     * @param offset
+     * @param count
+     * @return true if the entire message is ActiveSensing commands
+     */
+    public static boolean isAllActiveSensing(byte[] msg, int offset,
+            int count) {
+        // Count bytes that are not active sensing.
+        int goodBytes = 0;
+        for (int i = 0; i < count; i++) {
+            byte b = msg[offset + i];
+            if (b != MidiConstants.STATUS_ACTIVE_SENSING) {
+                goodBytes++;
+            }
+        }
+        return (goodBytes == 0);
+    }
+
+}
diff --git a/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/MidiDispatcher.java b/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/MidiDispatcher.java
new file mode 100644
index 0000000..b7f1fe1
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/MidiDispatcher.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2015 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.android.common.midi;
+
+import android.media.midi.MidiReceiver;
+import android.media.midi.MidiSender;
+
+import java.io.IOException;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * Utility class for dispatching MIDI data to a list of {@link MidiReceiver}s.
+ * This class subclasses {@link MidiReceiver} and dispatches any data it receives
+ * to its receiver list. Any receivers that throw an exception upon receiving data will
+ * be automatically removed from the receiver list, but no IOException will be returned
+ * from the dispatcher's {@link MidiReceiver#onReceive} in that case.
+ */
+public final class MidiDispatcher extends MidiReceiver {
+
+    private final CopyOnWriteArrayList<MidiReceiver> mReceivers
+            = new CopyOnWriteArrayList<MidiReceiver>();
+
+    private final MidiSender mSender = new MidiSender() {
+        /**
+         * Called to connect a {@link MidiReceiver} to the sender
+         *
+         * @param receiver the receiver to connect
+         */
+        @Override
+        public void onConnect(MidiReceiver receiver) {
+            mReceivers.add(receiver);
+        }
+
+        /**
+         * Called to disconnect a {@link MidiReceiver} from the sender
+         *
+         * @param receiver the receiver to disconnect
+         */
+        @Override
+        public void onDisconnect(MidiReceiver receiver) {
+            mReceivers.remove(receiver);
+        }
+    };
+
+    /**
+     * Returns the number of {@link MidiReceiver}s this dispatcher contains.
+     * @return the number of receivers
+     */
+    public int getReceiverCount() {
+        return mReceivers.size();
+    }
+
+    /**
+     * Returns a {@link MidiSender} which is used to add and remove
+     * {@link MidiReceiver}s
+     * to the dispatcher's receiver list.
+     * @return the dispatcher's MidiSender
+     */
+    public MidiSender getSender() {
+        return mSender;
+    }
+
+    @Override
+    public void onSend(byte[] msg, int offset, int count, long timestamp) throws IOException {
+       for (MidiReceiver receiver : mReceivers) {
+            try {
+                receiver.send(msg, offset, count, timestamp);
+            } catch (IOException e) {
+                // if the receiver fails we remove the receiver but do not propagate the exception
+                mReceivers.remove(receiver);
+            }
+        }
+    }
+
+    @Override
+    public void flush() throws IOException {
+       for (MidiReceiver receiver : mReceivers) {
+            receiver.flush();
+       }
+    }
+}
diff --git a/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/MidiEventScheduler.java b/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/MidiEventScheduler.java
new file mode 100644
index 0000000..513d393
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/MidiEventScheduler.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2015 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.android.common.midi;
+
+import android.media.midi.MidiReceiver;
+
+import java.io.IOException;
+
+/**
+ * Add MIDI Events to an EventScheduler
+ */
+public class MidiEventScheduler extends EventScheduler {
+    private static final String TAG = "MidiEventScheduler";
+    // Maintain a pool of scheduled events to reduce memory allocation.
+    // This pool increases performance by about 14%.
+    private final static int POOL_EVENT_SIZE = 16;
+    private MidiReceiver mReceiver = new SchedulingReceiver();
+
+    private class SchedulingReceiver extends MidiReceiver
+    {
+        /**
+         * Store these bytes in the EventScheduler to be delivered at the specified
+         * time.
+         */
+        @Override
+        public void onSend(byte[] msg, int offset, int count, long timestamp)
+                throws IOException {
+            MidiEvent event = createScheduledEvent(msg, offset, count, timestamp);
+            if (event != null) {
+                add(event);
+            }
+        }
+    }
+
+    public static class MidiEvent extends SchedulableEvent {
+        public int count = 0;
+        public byte[] data;
+
+        private MidiEvent(int count) {
+            super(0);
+            data = new byte[count];
+        }
+
+        private MidiEvent(byte[] msg, int offset, int count, long timestamp) {
+            super(timestamp);
+            data = new byte[count];
+            System.arraycopy(msg, offset, data, 0, count);
+            this.count = count;
+        }
+
+        @Override
+        public String toString() {
+            String text = "Event: ";
+            for (int i = 0; i < count; i++) {
+                text += data[i] + ", ";
+            }
+            return text;
+        }
+    }
+
+    /**
+     * Create an event that contains the message.
+     */
+    private MidiEvent createScheduledEvent(byte[] msg, int offset, int count,
+            long timestamp) {
+        MidiEvent event;
+        if (count > POOL_EVENT_SIZE) {
+            event = new MidiEvent(msg, offset, count, timestamp);
+        } else {
+            event = (MidiEvent) removeEventfromPool();
+            if (event == null) {
+                event = new MidiEvent(POOL_EVENT_SIZE);
+            }
+            System.arraycopy(msg, offset, event.data, 0, count);
+            event.count = count;
+            event.setTimestamp(timestamp);
+        }
+        return event;
+    }
+
+    /**
+     * Return events to a pool so they can be reused.
+     *
+     * @param event
+     */
+    @Override
+    public void addEventToPool(SchedulableEvent event) {
+        // Make sure the event is suitable for the pool.
+        if (event instanceof MidiEvent) {
+            MidiEvent midiEvent = (MidiEvent) event;
+            if (midiEvent.data.length == POOL_EVENT_SIZE) {
+                super.addEventToPool(event);
+            }
+        }
+    }
+
+    /**
+     * This MidiReceiver will write date to the scheduling buffer.
+     * @return the MidiReceiver
+     */
+    public MidiReceiver getReceiver() {
+        return mReceiver;
+    }
+
+}
diff --git a/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/MidiEventThread.java b/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/MidiEventThread.java
new file mode 100644
index 0000000..626e83c
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/MidiEventThread.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2015 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.android.common.midi;
+
+import android.media.midi.MidiSender;
+import android.util.Log;
+
+import java.io.IOException;
+
+public class MidiEventThread extends MidiEventScheduler {
+    protected static final String TAG = "MidiEventThread";
+
+    private EventThread mEventThread;
+    MidiDispatcher mDispatcher = new MidiDispatcher();
+
+    class EventThread extends Thread {
+        private boolean go = true;
+
+        @Override
+        public void run() {
+            while (go) {
+                try {
+                    MidiEvent event = (MidiEvent) waitNextEvent();
+                    try {
+                        Log.i(TAG, "Fire event " + event.data[0] + " at "
+                                + event.getTimestamp());
+                        mDispatcher.send(event.data, 0,
+                                event.count, event.getTimestamp());
+                    } catch (IOException e) {
+                        e.printStackTrace();
+                    }
+                    // Put event back in the pool for future use.
+                    addEventToPool(event);
+                } catch (InterruptedException e) {
+                    // OK, this is how we stop the thread.
+                }
+            }
+        }
+
+        /**
+         * Asynchronously tell the thread to stop.
+         */
+        public void requestStop() {
+            go = false;
+            interrupt();
+        }
+    }
+
+    public void start() {
+        stop();
+        mEventThread = new EventThread();
+        mEventThread.start();
+    }
+
+    /**
+     * Asks the thread to stop then waits for it to stop.
+     */
+    public void stop() {
+        if (mEventThread != null) {
+            mEventThread.requestStop();
+            try {
+                mEventThread.join(500);
+            } catch (InterruptedException e) {
+                Log.e(TAG,
+                        "Interrupted while waiting for MIDI EventScheduler thread to stop.");
+            } finally {
+                mEventThread = null;
+            }
+        }
+    }
+
+    public MidiSender getSender() {
+        return mDispatcher.getSender();
+    }
+
+}
diff --git a/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/MidiFramer.java b/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/MidiFramer.java
new file mode 100644
index 0000000..c274925
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/MidiFramer.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2015 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.android.common.midi;
+
+import android.media.midi.MidiReceiver;
+import android.util.Log;
+
+import java.io.IOException;
+
+/**
+ * Convert stream of arbitrary MIDI bytes into discrete messages.
+ *
+ * Parses the incoming bytes and then posts individual messages to the receiver
+ * specified in the constructor. Short messages of 1-3 bytes will be complete.
+ * System Exclusive messages may be posted in pieces.
+ *
+ * Resolves Running Status and interleaved System Real-Time messages.
+ */
+public class MidiFramer extends MidiReceiver {
+    private MidiReceiver mReceiver;
+    private byte[] mBuffer = new byte[3];
+    private int mCount;
+    private byte mRunningStatus;
+    private int mNeeded;
+    private boolean mInSysEx;
+
+    public MidiFramer(MidiReceiver receiver) {
+        mReceiver = receiver;
+    }
+
+    /*
+     * @see android.midi.MidiReceiver#onSend(byte[], int, int, long)
+     */
+    @Override
+    public void onSend(byte[] data, int offset, int count, long timestamp)
+            throws IOException {
+        int sysExStartOffset = (mInSysEx ? offset : -1);
+
+        for (int i = 0; i < count; i++) {
+            final byte currentByte = data[offset];
+            final int currentInt = currentByte & 0xFF;
+            if (currentInt >= 0x80) { // status byte?
+                if (currentInt < 0xF0) { // channel message?
+                    mRunningStatus = currentByte;
+                    mCount = 1;
+                    mNeeded = MidiConstants.getBytesPerMessage(currentByte) - 1;
+                } else if (currentInt < 0xF8) { // system common?
+                    if (currentInt == 0xF0 /* SysEx Start */) {
+                        // Log.i(TAG, "SysEx Start");
+                        mInSysEx = true;
+                        sysExStartOffset = offset;
+                    } else if (currentInt == 0xF7 /* SysEx End */) {
+                        // Log.i(TAG, "SysEx End");
+                        if (mInSysEx) {
+                            mReceiver.send(data, sysExStartOffset,
+                                offset - sysExStartOffset + 1, timestamp);
+                            mInSysEx = false;
+                            sysExStartOffset = -1;
+                        }
+                    } else {
+                        mBuffer[0] = currentByte;
+                        mRunningStatus = 0;
+                        mCount = 1;
+                        mNeeded = MidiConstants.getBytesPerMessage(currentByte) - 1;
+                    }
+                } else { // real-time?
+                    // Single byte message interleaved with other data.
+                    if (mInSysEx) {
+                        mReceiver.send(data, sysExStartOffset,
+                                offset - sysExStartOffset, timestamp);
+                        sysExStartOffset = offset + 1;
+                    }
+                    mReceiver.send(data, offset, 1, timestamp);
+                }
+            } else { // data byte
+                if (!mInSysEx) {
+                    mBuffer[mCount++] = currentByte;
+                    if (--mNeeded == 0) {
+                        if (mRunningStatus != 0) {
+                            mBuffer[0] = mRunningStatus;
+                        }
+                        mReceiver.send(mBuffer, 0, mCount, timestamp);
+                        mNeeded = MidiConstants.getBytesPerMessage(mBuffer[0]) - 1;
+                        mCount = 1;
+                    }
+                }
+            }
+            ++offset;
+        }
+
+        // send any accumulatedSysEx data
+        if (sysExStartOffset >= 0 && sysExStartOffset < offset) {
+            mReceiver.send(data, sysExStartOffset,
+                    offset - sysExStartOffset, timestamp);
+        }
+    }
+
+}
diff --git a/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/MidiInputPortSelector.java b/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/MidiInputPortSelector.java
new file mode 100644
index 0000000..7c665ba
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/MidiInputPortSelector.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2015 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.android.common.midi;
+
+import android.app.Activity;
+import android.media.midi.MidiDevice;
+import android.media.midi.MidiDeviceInfo;
+import android.media.midi.MidiInputPort;
+import android.media.midi.MidiManager;
+import android.media.midi.MidiReceiver;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+
+import java.io.IOException;
+
+/**
+ * Manages a Spinner for selecting a MidiInputPort.
+ */
+public class MidiInputPortSelector extends MidiPortSelector {
+
+    private MidiInputPort mInputPort;
+    private MidiDevice mOpenDevice;
+
+    /**
+     * @param midiManager
+     * @param activity
+     * @param spinnerId ID from the layout resource
+     */
+    public MidiInputPortSelector(MidiManager midiManager, Activity activity,
+            int spinnerId) {
+        super(midiManager, activity, spinnerId, MidiDeviceInfo.PortInfo.TYPE_INPUT);
+    }
+
+    @Override
+    public void onPortSelected(final MidiPortWrapper wrapper) {
+        close();
+        final MidiDeviceInfo info = wrapper.getDeviceInfo();
+        if (info != null) {
+            mMidiManager.openDevice(info, new MidiManager.OnDeviceOpenedListener() {
+                    @Override
+                public void onDeviceOpened(MidiDevice device) {
+                    if (device == null) {
+                        Log.e(MidiConstants.TAG, "could not open " + info);
+                    } else {
+                        mOpenDevice = device;
+                        mInputPort = mOpenDevice.openInputPort(
+                                wrapper.getPortIndex());
+                        if (mInputPort == null) {
+                            Log.e(MidiConstants.TAG, "could not open input port on " + info);
+                        }
+                    }
+                }
+            }, null);
+            // Don't run the callback on the UI thread because openInputPort might take a while.
+        }
+    }
+
+    public MidiReceiver getReceiver() {
+        return mInputPort;
+    }
+
+    @Override
+    public void onClose() {
+        try {
+            if (mInputPort != null) {
+                Log.i(MidiConstants.TAG, "MidiInputPortSelector.onClose() - close port");
+                mInputPort.close();
+            }
+            mInputPort = null;
+            if (mOpenDevice != null) {
+                mOpenDevice.close();
+            }
+            mOpenDevice = null;
+        } catch (IOException e) {
+            Log.e(MidiConstants.TAG, "cleanup failed", e);
+        }
+    }
+}
diff --git a/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/MidiOutputPortConnectionSelector.java b/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/MidiOutputPortConnectionSelector.java
new file mode 100644
index 0000000..ca1ade4
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/MidiOutputPortConnectionSelector.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2015 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.android.common.midi;
+
+import android.app.Activity;
+import android.media.midi.MidiDeviceInfo;
+import android.media.midi.MidiManager;
+import android.util.Log;
+
+import java.io.IOException;
+
+/**
+ * Select an output port and connect it to a destination input port.
+ */
+public class MidiOutputPortConnectionSelector extends MidiPortSelector {
+
+    private MidiPortConnector mSynthConnector;
+    private MidiDeviceInfo mDestinationDeviceInfo;
+    private int mDestinationPortIndex;
+    private MidiPortConnector.OnPortsConnectedListener mConnectedListener;
+
+    /**
+     * @param midiManager
+     * @param activity
+     * @param spinnerId
+     * @param type
+     */
+    public MidiOutputPortConnectionSelector(MidiManager midiManager,
+            Activity activity, int spinnerId,
+            MidiDeviceInfo destinationDeviceInfo, int destinationPortIndex) {
+        super(midiManager, activity, spinnerId,
+                MidiDeviceInfo.PortInfo.TYPE_OUTPUT);
+        mDestinationDeviceInfo = destinationDeviceInfo;
+        mDestinationPortIndex = destinationPortIndex;
+    }
+
+    @Override
+    public void onPortSelected(final MidiPortWrapper wrapper) {
+        Log.i(MidiConstants.TAG, "connectPortToSynth: " + wrapper);
+        onClose();
+        if (wrapper.getDeviceInfo() != null) {
+            mSynthConnector = new MidiPortConnector(mMidiManager);
+            mSynthConnector.connectToDevicePort(wrapper.getDeviceInfo(),
+                    wrapper.getPortIndex(), mDestinationDeviceInfo,
+                    mDestinationPortIndex,
+                    // not safe on UI thread
+                    mConnectedListener, null);
+        }
+    }
+
+    @Override
+    public void onClose() {
+        try {
+            if (mSynthConnector != null) {
+                mSynthConnector.close();
+                mSynthConnector = null;
+            }
+        } catch (IOException e) {
+            Log.e(MidiConstants.TAG, "Exception in closeSynthResources()", e);
+        }
+    }
+
+    /**
+     * @param myPortsConnectedListener
+     */
+    public void setConnectedListener(
+            MidiPortConnector.OnPortsConnectedListener connectedListener) {
+        mConnectedListener = connectedListener;
+    }
+}
diff --git a/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/MidiOutputPortSelector.java b/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/MidiOutputPortSelector.java
new file mode 100644
index 0000000..5aebf72
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/MidiOutputPortSelector.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2015 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.android.common.midi;
+
+import android.app.Activity;
+import android.media.midi.MidiDevice;
+import android.media.midi.MidiDeviceInfo;
+import android.media.midi.MidiManager;
+import android.media.midi.MidiOutputPort;
+import android.media.midi.MidiSender;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+
+import java.io.IOException;
+
+/**
+ * Manages a Spinner for selecting a MidiOutputPort.
+ */
+public class MidiOutputPortSelector extends MidiPortSelector {
+    private MidiOutputPort mOutputPort;
+    private MidiDispatcher mDispatcher = new MidiDispatcher();
+    private MidiDevice mOpenDevice;
+
+    /**
+     * @param midiManager
+     * @param activity
+     * @param spinnerId ID from the layout resource
+     */
+    public MidiOutputPortSelector(MidiManager midiManager, Activity activity,
+            int spinnerId) {
+        super(midiManager, activity, spinnerId, MidiDeviceInfo.PortInfo.TYPE_OUTPUT);
+    }
+
+    @Override
+    public void onPortSelected(final MidiPortWrapper wrapper) {
+        Log.i(MidiConstants.TAG, "onPortSelected: " + wrapper);
+        close();
+
+        final MidiDeviceInfo info = wrapper.getDeviceInfo();
+        if (info != null) {
+            mMidiManager.openDevice(info, new MidiManager.OnDeviceOpenedListener() {
+
+                    @Override
+                public void onDeviceOpened(MidiDevice device) {
+                    if (device == null) {
+                        Log.e(MidiConstants.TAG, "could not open " + info);
+                    } else {
+                        mOpenDevice = device;
+                        mOutputPort = device.openOutputPort(wrapper.getPortIndex());
+                        if (mOutputPort == null) {
+                            Log.e(MidiConstants.TAG,
+                                    "could not open output port for " + info);
+                            return;
+                        }
+                        mOutputPort.connect(mDispatcher);
+                    }
+                }
+            }, null);
+            // Don't run the callback on the UI thread because openOutputPort might take a while.
+        }
+    }
+
+    @Override
+    public void onClose() {
+        try {
+            if (mOutputPort != null) {
+                mOutputPort.disconnect(mDispatcher);
+            }
+            mOutputPort = null;
+            if (mOpenDevice != null) {
+                mOpenDevice.close();
+            }
+            mOpenDevice = null;
+        } catch (IOException e) {
+            Log.e(MidiConstants.TAG, "cleanup failed", e);
+        }
+    }
+
+    /**
+     * You can connect your MidiReceivers to this sender. The user will then select which output
+     * port will send messages through this MidiSender.
+     * @return a MidiSender that will send the messages from the selected port.
+     */
+    public MidiSender getSender() {
+        return mDispatcher.getSender();
+    }
+
+}
diff --git a/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/MidiPortConnector.java b/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/MidiPortConnector.java
new file mode 100644
index 0000000..457494d
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/MidiPortConnector.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2015 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.android.common.midi;
+
+import android.media.midi.MidiDevice;
+import android.media.midi.MidiDevice.MidiConnection;
+import android.media.midi.MidiDeviceInfo;
+import android.media.midi.MidiInputPort;
+import android.media.midi.MidiManager;
+import android.os.Handler;
+import android.util.Log;
+
+import java.io.IOException;
+
+/**
+ * Tool for connecting MIDI ports on two remote devices.
+ */
+public class MidiPortConnector {
+    private final MidiManager mMidiManager;
+    private MidiDevice mSourceDevice;
+    private MidiDevice mDestinationDevice;
+    private MidiConnection mConnection;
+
+    /**
+     * @param mMidiManager
+     */
+    public MidiPortConnector(MidiManager midiManager) {
+        mMidiManager = midiManager;
+    }
+
+    public void close() throws IOException {
+        if (mConnection != null) {
+            Log.i(MidiConstants.TAG,
+                    "MidiPortConnector closing connection " + mConnection);
+            mConnection.close();
+            mConnection = null;
+        }
+        if (mSourceDevice != null) {
+            mSourceDevice.close();
+            mSourceDevice = null;
+        }
+        if (mDestinationDevice != null) {
+            mDestinationDevice.close();
+            mDestinationDevice = null;
+        }
+    }
+
+    private void safeClose() {
+        try {
+            close();
+        } catch (IOException e) {
+            Log.e(MidiConstants.TAG, "could not close resources", e);
+        }
+    }
+
+    /**
+     * Listener class used for receiving the results of
+     * {@link #connectToDevicePort}
+     */
+    public interface OnPortsConnectedListener {
+        /**
+         * Called to respond to a {@link #connectToDevicePort} request
+         *
+         * @param connection
+         *            a {@link MidiConnection} that represents the connected
+         *            ports, or null if connection failed
+         */
+        abstract public void onPortsConnected(MidiConnection connection);
+    }
+
+    /**
+     * Open two devices and connect their ports.
+     *
+     * @param sourceDeviceInfo
+     * @param sourcePortIndex
+     * @param destinationDeviceInfo
+     * @param destinationPortIndex
+     */
+    public void connectToDevicePort(final MidiDeviceInfo sourceDeviceInfo,
+            final int sourcePortIndex,
+            final MidiDeviceInfo destinationDeviceInfo,
+            final int destinationPortIndex) {
+        connectToDevicePort(sourceDeviceInfo, sourcePortIndex,
+                destinationDeviceInfo, destinationPortIndex, null, null);
+    }
+
+    /**
+     * Open two devices and connect their ports.
+     *
+     * @param sourceDeviceInfo
+     * @param sourcePortIndex
+     * @param destinationDeviceInfo
+     * @param destinationPortIndex
+     */
+    public void connectToDevicePort(final MidiDeviceInfo sourceDeviceInfo,
+            final int sourcePortIndex,
+            final MidiDeviceInfo destinationDeviceInfo,
+            final int destinationPortIndex,
+            final OnPortsConnectedListener listener, final Handler handler) {
+        safeClose();
+        mMidiManager.openDevice(destinationDeviceInfo,
+                new MidiManager.OnDeviceOpenedListener() {
+                    @Override
+                    public void onDeviceOpened(MidiDevice destinationDevice) {
+                        if (destinationDevice == null) {
+                            Log.e(MidiConstants.TAG,
+                                    "could not open " + destinationDeviceInfo);
+                            if (listener != null) {
+                                listener.onPortsConnected(null);
+                            }
+                        } else {
+                            mDestinationDevice = destinationDevice;
+                            Log.i(MidiConstants.TAG,
+                                    "connectToDevicePort opened "
+                                            + destinationDeviceInfo);
+                            // Destination device was opened so go to next step.
+                            MidiInputPort destinationInputPort = destinationDevice
+                                    .openInputPort(destinationPortIndex);
+                            if (destinationInputPort != null) {
+                                Log.i(MidiConstants.TAG,
+                                        "connectToDevicePort opened port on "
+                                                + destinationDeviceInfo);
+                                connectToDevicePort(sourceDeviceInfo,
+                                        sourcePortIndex,
+                                        destinationInputPort,
+                                        listener, handler);
+                            } else {
+                                Log.e(MidiConstants.TAG,
+                                        "could not open port on "
+                                                + destinationDeviceInfo);
+                                safeClose();
+                                if (listener != null) {
+                                    listener.onPortsConnected(null);
+                                }
+                            }
+                        }
+                    }
+                }, handler);
+    }
+
+
+    /**
+     * Open a source device and connect its output port to the
+     * destinationInputPort.
+     *
+     * @param sourceDeviceInfo
+     * @param sourcePortIndex
+     * @param destinationInputPort
+     */
+    private void connectToDevicePort(final MidiDeviceInfo sourceDeviceInfo,
+            final int sourcePortIndex,
+            final MidiInputPort destinationInputPort,
+            final OnPortsConnectedListener listener, final Handler handler) {
+        mMidiManager.openDevice(sourceDeviceInfo,
+                new MidiManager.OnDeviceOpenedListener() {
+                    @Override
+                    public void onDeviceOpened(MidiDevice device) {
+                        if (device == null) {
+                            Log.e(MidiConstants.TAG,
+                                    "could not open " + sourceDeviceInfo);
+                            safeClose();
+                            if (listener != null) {
+                                listener.onPortsConnected(null);
+                            }
+                        } else {
+                            Log.i(MidiConstants.TAG,
+                                    "connectToDevicePort opened "
+                                            + sourceDeviceInfo);
+                            // Device was opened so connect the ports.
+                            mSourceDevice = device;
+                            mConnection = device.connectPorts(
+                                    destinationInputPort, sourcePortIndex);
+                            if (mConnection == null) {
+                                Log.e(MidiConstants.TAG, "could not connect to "
+                                        + sourceDeviceInfo);
+                                safeClose();
+                            }
+                            if (listener != null) {
+                                listener.onPortsConnected(mConnection);
+                            }
+                        }
+                    }
+                }, handler);
+    }
+
+}
diff --git a/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/MidiPortSelector.java b/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/MidiPortSelector.java
new file mode 100644
index 0000000..39f983e
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/MidiPortSelector.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2015 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.android.common.midi;
+
+import android.app.Activity;
+import android.media.midi.MidiDeviceInfo;
+import android.media.midi.MidiDeviceStatus;
+import android.media.midi.MidiManager;
+import android.media.midi.MidiManager.DeviceCallback;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Spinner;
+
+import java.util.HashSet;
+
+/**
+ * Base class that uses a Spinner to select available MIDI ports.
+ */
+public abstract class MidiPortSelector extends DeviceCallback {
+    private int mType = MidiDeviceInfo.PortInfo.TYPE_INPUT;
+    protected ArrayAdapter<MidiPortWrapper> mAdapter;
+    protected HashSet<MidiPortWrapper> mBusyPorts = new HashSet<MidiPortWrapper>();
+    private Spinner mSpinner;
+    protected MidiManager mMidiManager;
+    protected Activity mActivity;
+    private MidiPortWrapper mCurrentWrapper;
+
+    /**
+     * @param midiManager
+     * @param activity
+     * @param spinnerId
+     *            ID from the layout resource
+     * @param type
+     *            TYPE_INPUT or TYPE_OUTPUT
+     */
+    public MidiPortSelector(MidiManager midiManager, Activity activity,
+            int spinnerId, int type) {
+        mMidiManager = midiManager;
+        mActivity = activity;
+        mType = type;
+        mAdapter = new ArrayAdapter<MidiPortWrapper>(activity,
+                android.R.layout.simple_spinner_item);
+        mAdapter.setDropDownViewResource(
+                android.R.layout.simple_spinner_dropdown_item);
+        mAdapter.add(new MidiPortWrapper(null, 0, 0));
+
+        mSpinner = (Spinner) activity.findViewById(spinnerId);
+        mSpinner.setOnItemSelectedListener(
+                new AdapterView.OnItemSelectedListener() {
+
+                    public void onItemSelected(AdapterView<?> parent, View view,
+                            int pos, long id) {
+                        mCurrentWrapper = mAdapter.getItem(pos);
+                        onPortSelected(mCurrentWrapper);
+                    }
+
+                    public void onNothingSelected(AdapterView<?> parent) {
+                        onPortSelected(null);
+                        mCurrentWrapper = null;
+                    }
+                });
+        mSpinner.setAdapter(mAdapter);
+
+        mMidiManager.registerDeviceCallback(this,
+                new Handler(Looper.getMainLooper()));
+
+        MidiDeviceInfo[] infos = mMidiManager.getDevices();
+        for (MidiDeviceInfo info : infos) {
+            onDeviceAdded(info);
+        }
+    }
+
+    /**
+     * Set to no port selected.
+     */
+    public void clearSelection() {
+        mSpinner.setSelection(0);
+    }
+
+    private int getInfoPortCount(final MidiDeviceInfo info) {
+        int portCount = (mType == MidiDeviceInfo.PortInfo.TYPE_INPUT)
+                ? info.getInputPortCount() : info.getOutputPortCount();
+        return portCount;
+    }
+
+    @Override
+    public void onDeviceAdded(final MidiDeviceInfo info) {
+        int portCount = getInfoPortCount(info);
+        for (int i = 0; i < portCount; ++i) {
+            MidiPortWrapper wrapper = new MidiPortWrapper(info, mType, i);
+            mAdapter.add(wrapper);
+            Log.i(MidiConstants.TAG, wrapper + " was added");
+            mAdapter.notifyDataSetChanged();
+        }
+    }
+
+    @Override
+    public void onDeviceRemoved(final MidiDeviceInfo info) {
+        int portCount = getInfoPortCount(info);
+        for (int i = 0; i < portCount; ++i) {
+            MidiPortWrapper wrapper = new MidiPortWrapper(info, mType, i);
+            MidiPortWrapper currentWrapper = mCurrentWrapper;
+            mAdapter.remove(wrapper);
+            // If the currently selected port was removed then select no port.
+            if (wrapper.equals(currentWrapper)) {
+                clearSelection();
+            }
+            mAdapter.notifyDataSetChanged();
+            Log.i(MidiConstants.TAG, wrapper + " was removed");
+        }
+    }
+
+    @Override
+    public void onDeviceStatusChanged(final MidiDeviceStatus status) {
+        // If an input port becomes busy then remove it from the menu.
+        // If it becomes free then add it back to the menu.
+        if (mType == MidiDeviceInfo.PortInfo.TYPE_INPUT) {
+            MidiDeviceInfo info = status.getDeviceInfo();
+            Log.i(MidiConstants.TAG, "MidiPortSelector.onDeviceStatusChanged status = " + status
+                    + ", mType = " + mType
+                    + ", activity = " + mActivity.getPackageName()
+                    + ", info = " + info);
+            // Look for transitions from free to busy.
+            int portCount = info.getInputPortCount();
+            for (int i = 0; i < portCount; ++i) {
+                MidiPortWrapper wrapper = new MidiPortWrapper(info, mType, i);
+                if (!wrapper.equals(mCurrentWrapper)) {
+                    if (status.isInputPortOpen(i)) { // busy?
+                        if (!mBusyPorts.contains(wrapper)) {
+                            // was free, now busy
+                            mBusyPorts.add(wrapper);
+                            mAdapter.remove(wrapper);
+                            mAdapter.notifyDataSetChanged();
+                        }
+                    } else {
+                        if (mBusyPorts.remove(wrapper)) {
+                            // was busy, now free
+                            mAdapter.add(wrapper);
+                            mAdapter.notifyDataSetChanged();
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Implement this method to handle the user selecting a port on a device.
+     *
+     * @param wrapper
+     */
+    public abstract void onPortSelected(MidiPortWrapper wrapper);
+
+    /**
+     * Implement this method to clean up any open resources.
+     */
+    public abstract void onClose();
+
+    /**
+     *
+     */
+    public void close() {
+        onClose();
+    }
+}
diff --git a/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/MidiPortWrapper.java b/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/MidiPortWrapper.java
new file mode 100644
index 0000000..77aa734
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/MidiPortWrapper.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2015 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.android.common.midi;
+
+import android.media.midi.MidiDeviceInfo;
+import android.media.midi.MidiDeviceInfo.PortInfo;
+import android.util.Log;
+
+// Wrapper for a MIDI device and port description.
+public class MidiPortWrapper {
+    private MidiDeviceInfo mInfo;
+    private int mPortIndex;
+    private int mType;
+    private String mString;
+
+    /**
+     * Wrapper for a MIDI device and port description.
+     * @param info
+     * @param portType
+     * @param portIndex
+     */
+    public MidiPortWrapper(MidiDeviceInfo info, int portType, int portIndex) {
+        mInfo = info;
+        mType = portType;
+        mPortIndex = portIndex;
+    }
+
+    private void updateString() {
+        if (mInfo == null) {
+            mString = "- - - - - -";
+        } else {
+            StringBuilder sb = new StringBuilder();
+            String name = mInfo.getProperties()
+                    .getString(MidiDeviceInfo.PROPERTY_NAME);
+            if (name == null) {
+                name = mInfo.getProperties()
+                        .getString(MidiDeviceInfo.PROPERTY_MANUFACTURER) + ", "
+                        + mInfo.getProperties()
+                                .getString(MidiDeviceInfo.PROPERTY_PRODUCT);
+            }
+            sb.append("#" + mInfo.getId());
+            sb.append(", ").append(name);
+            PortInfo portInfo = findPortInfo();
+            sb.append("[" + mPortIndex + "]");
+            if (portInfo != null) {
+                sb.append(", ").append(portInfo.getName());
+            } else {
+                sb.append(", null");
+            }
+            mString = sb.toString();
+        }
+    }
+
+    /**
+     * @param info
+     * @param portIndex
+     * @return
+     */
+    private PortInfo findPortInfo() {
+        PortInfo[] ports = mInfo.getPorts();
+        for (PortInfo portInfo : ports) {
+            if (portInfo.getPortNumber() == mPortIndex
+                    && portInfo.getType() == mType) {
+                return portInfo;
+            }
+        }
+        return null;
+    }
+
+    public int getPortIndex() {
+        return mPortIndex;
+    }
+
+    public MidiDeviceInfo getDeviceInfo() {
+        return mInfo;
+    }
+
+    @Override
+    public String toString() {
+        if (mString == null) {
+            updateString();
+        }
+        return mString;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (other == null)
+            return false;
+        if (!(other instanceof MidiPortWrapper))
+            return false;
+        MidiPortWrapper otherWrapper = (MidiPortWrapper) other;
+        if (mPortIndex != otherWrapper.mPortIndex)
+            return false;
+        if (mType != otherWrapper.mType)
+            return false;
+        if (mInfo == null)
+            return (otherWrapper.mInfo == null);
+        return mInfo.equals(otherWrapper.mInfo);
+    }
+
+    @Override
+    public int hashCode() {
+        int hashCode = 1;
+        hashCode = 31 * hashCode + mPortIndex;
+        hashCode = 31 * hashCode + mType;
+        hashCode = 31 * hashCode + mInfo.hashCode();
+        return hashCode;
+    }
+
+}
diff --git a/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/MidiTools.java b/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/MidiTools.java
new file mode 100644
index 0000000..82e3de4
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/MidiTools.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2015 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.android.common.midi;
+
+import android.media.midi.MidiDeviceInfo;
+import android.media.midi.MidiManager;
+
+/**
+ * Miscellaneous tools for Android MIDI.
+ */
+public class MidiTools {
+
+    /**
+     * @return a device that matches the manufacturer and product or null
+     */
+    public static MidiDeviceInfo findDevice(MidiManager midiManager,
+            String manufacturer, String product) {
+        for (MidiDeviceInfo info : midiManager.getDevices()) {
+            String deviceManufacturer = info.getProperties()
+                    .getString(MidiDeviceInfo.PROPERTY_MANUFACTURER);
+            if ((manufacturer != null)
+                    && manufacturer.equals(deviceManufacturer)) {
+                String deviceProduct = info.getProperties()
+                        .getString(MidiDeviceInfo.PROPERTY_PRODUCT);
+                if ((product != null) && product.equals(deviceProduct)) {
+                    return info;
+                }
+            }
+        }
+        return null;
+    }
+}
diff --git a/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/synth/EnvelopeADSR.java b/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/synth/EnvelopeADSR.java
new file mode 100644
index 0000000..a29a193
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/synth/EnvelopeADSR.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2015 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.android.common.midi.synth;
+
+/**
+ * Very simple Attack, Decay, Sustain, Release envelope with linear ramps.
+ *
+ * Times are in seconds.
+ */
+public class EnvelopeADSR extends SynthUnit {
+    private static final int IDLE = 0;
+    private static final int ATTACK = 1;
+    private static final int DECAY = 2;
+    private static final int SUSTAIN = 3;
+    private static final int RELEASE = 4;
+    private static final int FINISHED = 5;
+    private static final float MIN_TIME = 0.001f;
+
+    private float mAttackRate;
+    private float mRreleaseRate;
+    private float mSustainLevel;
+    private float mDecayRate;
+    private float mCurrent;
+    private int mSstate = IDLE;
+
+    public EnvelopeADSR() {
+        setAttackTime(0.003f);
+        setDecayTime(0.08f);
+        setSustainLevel(0.3f);
+        setReleaseTime(1.0f);
+    }
+
+    public void setAttackTime(float time) {
+        if (time < MIN_TIME)
+            time = MIN_TIME;
+        mAttackRate = 1.0f / (SynthEngine.FRAME_RATE * time);
+    }
+
+    public void setDecayTime(float time) {
+        if (time < MIN_TIME)
+            time = MIN_TIME;
+        mDecayRate = 1.0f / (SynthEngine.FRAME_RATE * time);
+    }
+
+    public void setSustainLevel(float level) {
+        if (level < 0.0f)
+            level = 0.0f;
+        mSustainLevel = level;
+    }
+
+    public void setReleaseTime(float time) {
+        if (time < MIN_TIME)
+            time = MIN_TIME;
+        mRreleaseRate = 1.0f / (SynthEngine.FRAME_RATE * time);
+    }
+
+    public void on() {
+        mSstate = ATTACK;
+    }
+
+    public void off() {
+        mSstate = RELEASE;
+    }
+
+    @Override
+    public float render() {
+        switch (mSstate) {
+        case ATTACK:
+            mCurrent += mAttackRate;
+            if (mCurrent > 1.0f) {
+                mCurrent = 1.0f;
+                mSstate = DECAY;
+            }
+            break;
+        case DECAY:
+            mCurrent -= mDecayRate;
+            if (mCurrent < mSustainLevel) {
+                mCurrent = mSustainLevel;
+                mSstate = SUSTAIN;
+            }
+            break;
+        case RELEASE:
+            mCurrent -= mRreleaseRate;
+            if (mCurrent < 0.0f) {
+                mCurrent = 0.0f;
+                mSstate = FINISHED;
+            }
+            break;
+        }
+        return mCurrent;
+    }
+
+    public boolean isDone() {
+        return mSstate == FINISHED;
+    }
+
+}
diff --git a/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/synth/SawOscillator.java b/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/synth/SawOscillator.java
new file mode 100644
index 0000000..c02a6a1
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/synth/SawOscillator.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2015 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.android.common.midi.synth;
+
+public class SawOscillator extends SynthUnit {
+    private float mPhase = 0.0f;
+    private float mPhaseIncrement = 0.01f;
+    private float mFrequency = 0.0f;
+    private float mFrequencyScaler = 1.0f;
+    private float mAmplitude = 1.0f;
+
+    public void setPitch(float pitch) {
+        float freq = (float) pitchToFrequency(pitch);
+        setFrequency(freq);
+    }
+
+    public void setFrequency(float frequency) {
+        mFrequency = frequency;
+        updatePhaseIncrement();
+    }
+
+    private void updatePhaseIncrement() {
+        mPhaseIncrement = 2.0f * mFrequency * mFrequencyScaler / 48000.0f;
+    }
+
+    public void setAmplitude(float amplitude) {
+        mAmplitude = amplitude;
+    }
+
+    public float getAmplitude() {
+        return mAmplitude;
+    }
+
+    public float getFrequencyScaler() {
+        return mFrequencyScaler;
+    }
+
+    public void setFrequencyScaler(float frequencyScaler) {
+        mFrequencyScaler = frequencyScaler;
+        updatePhaseIncrement();
+    }
+
+    float incrementWrapPhase() {
+        mPhase += mPhaseIncrement;
+        while (mPhase > 1.0) {
+            mPhase -= 2.0;
+        }
+        while (mPhase < -1.0) {
+            mPhase += 2.0;
+        }
+        return mPhase;
+    }
+
+    @Override
+    public float render() {
+        return incrementWrapPhase() * mAmplitude;
+    }
+
+}
diff --git a/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/synth/SawOscillatorDPW.java b/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/synth/SawOscillatorDPW.java
new file mode 100644
index 0000000..e5d661d
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/synth/SawOscillatorDPW.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2015 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.android.common.midi.synth;
+
+/**
+ * Band limited sawtooth oscillator.
+ * This will have very little aliasing at high frequencies.
+ */
+public class SawOscillatorDPW extends SawOscillator {
+    private float mZ1 = 0.0f; // delayed values
+    private float mZ2 = 0.0f;
+    private float mScaler; // frequency dependent scaler
+    private final static float VERY_LOW_FREQ = 0.0000001f;
+
+    @Override
+    public void setFrequency(float freq) {
+        /* Calculate scaling based on frequency. */
+        freq = Math.abs(freq);
+        super.setFrequency(freq);
+        if (freq < VERY_LOW_FREQ) {
+            mScaler = (float) (0.125 * 44100 / VERY_LOW_FREQ);
+        } else {
+            mScaler = (float) (0.125 * 44100 / freq);
+        }
+    }
+
+    @Override
+    public float render() {
+        float phase = incrementWrapPhase();
+        /* Square the raw sawtooth. */
+        float squared = phase * phase;
+        float diffed = squared - mZ2;
+        mZ2 = mZ1;
+        mZ1 = squared;
+        return diffed * mScaler * getAmplitude();
+    }
+
+}
diff --git a/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/synth/SawVoice.java b/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/synth/SawVoice.java
new file mode 100644
index 0000000..3b3e543
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/synth/SawVoice.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2015 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.android.common.midi.synth;
+
+/**
+ * Sawtooth oscillator with an ADSR.
+ */
+public class SawVoice extends SynthVoice {
+    private SawOscillator mOscillator;
+    private EnvelopeADSR mEnvelope;
+
+    public SawVoice() {
+        mOscillator = createOscillator();
+        mEnvelope = new EnvelopeADSR();
+    }
+
+    protected SawOscillator createOscillator() {
+        return new SawOscillator();
+    }
+
+    @Override
+    public void noteOn(int noteIndex, int velocity) {
+        super.noteOn(noteIndex, velocity);
+        mOscillator.setPitch(noteIndex);
+        mOscillator.setAmplitude(getAmplitude());
+        mEnvelope.on();
+    }
+
+    @Override
+    public void noteOff() {
+        super.noteOff();
+        mEnvelope.off();
+    }
+
+    @Override
+    public void setFrequencyScaler(float scaler) {
+        mOscillator.setFrequencyScaler(scaler);
+    }
+
+    @Override
+    public float render() {
+        float output = mOscillator.render() * mEnvelope.render();
+        return output;
+    }
+
+    @Override
+    public boolean isDone() {
+        return mEnvelope.isDone();
+    }
+
+}
diff --git a/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/synth/SimpleAudioOutput.java b/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/synth/SimpleAudioOutput.java
new file mode 100644
index 0000000..04aa19c
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/synth/SimpleAudioOutput.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2015 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.android.common.midi.synth;
+
+import android.media.AudioFormat;
+import android.media.AudioManager;
+import android.media.AudioTrack;
+import android.util.Log;
+
+/**
+ * Simple base class for implementing audio output for examples.
+ * This can be sub-classed for experimentation or to redirect audio output.
+ */
+public class SimpleAudioOutput {
+
+    private static final String TAG = "AudioOutputTrack";
+    public static final int SAMPLES_PER_FRAME = 2;
+    public static final int BYTES_PER_SAMPLE = 4; // float
+    public static final int BYTES_PER_FRAME = SAMPLES_PER_FRAME * BYTES_PER_SAMPLE;
+    private AudioTrack mAudioTrack;
+    private int mFrameRate;
+
+    /**
+     *
+     */
+    public SimpleAudioOutput() {
+        super();
+    }
+
+    /**
+     * Create an audio track then call play().
+     *
+     * @param frameRate
+     */
+    public void start(int frameRate) {
+        stop();
+        mFrameRate = frameRate;
+        mAudioTrack = createAudioTrack(frameRate);
+        // AudioTrack will wait until it has enough data before starting.
+        mAudioTrack.play();
+    }
+
+    public AudioTrack createAudioTrack(int frameRate) {
+        int minBufferSizeBytes = AudioTrack.getMinBufferSize(frameRate,
+                AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_FLOAT);
+        Log.i(TAG, "AudioTrack.minBufferSize = " + minBufferSizeBytes
+                + " bytes = " + (minBufferSizeBytes / BYTES_PER_FRAME)
+                + " frames");
+        int bufferSize = 8 * minBufferSizeBytes / 8;
+        int outputBufferSizeFrames = bufferSize / BYTES_PER_FRAME;
+        Log.i(TAG, "actual bufferSize = " + bufferSize + " bytes = "
+                + outputBufferSizeFrames + " frames");
+
+        AudioTrack player = new AudioTrack(AudioManager.STREAM_MUSIC,
+                mFrameRate, AudioFormat.CHANNEL_OUT_STEREO,
+                AudioFormat.ENCODING_PCM_FLOAT, bufferSize,
+                AudioTrack.MODE_STREAM);
+        Log.i(TAG, "created AudioTrack");
+        return player;
+    }
+
+    public int write(float[] buffer, int offset, int length) {
+        return mAudioTrack.write(buffer, offset, length,
+                AudioTrack.WRITE_BLOCKING);
+    }
+
+    public void stop() {
+        if (mAudioTrack != null) {
+            mAudioTrack.stop();
+            mAudioTrack = null;
+        }
+    }
+
+    public int getFrameRate() {
+        return mFrameRate;
+    }
+
+    public AudioTrack getAudioTrack() {
+        return mAudioTrack;
+    }
+}
diff --git a/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/synth/SineOscillator.java b/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/synth/SineOscillator.java
new file mode 100644
index 0000000..c638c34
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/synth/SineOscillator.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2015 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.android.common.midi.synth;
+
+/**
+ * Sinewave oscillator.
+ */
+public class SineOscillator extends SawOscillator {
+    // Factorial constants.
+    private static final float IF3 = 1.0f / (2 * 3);
+    private static final float IF5 = IF3 / (4 * 5);
+    private static final float IF7 = IF5 / (6 * 7);
+    private static final float IF9 = IF7 / (8 * 9);
+    private static final float IF11 = IF9 / (10 * 11);
+
+    /**
+     * Calculate sine using Taylor expansion. Do not use values outside the range.
+     *
+     * @param currentPhase in the range of -1.0 to +1.0 for one cycle
+     */
+    public static float fastSin(float currentPhase) {
+
+        /* Wrap phase back into region where results are more accurate. */
+        float yp = (currentPhase > 0.5f) ? 1.0f - currentPhase 
+                : ((currentPhase < (-0.5f)) ? (-1.0f) - currentPhase : currentPhase);
+
+        float x = (float) (yp * Math.PI);
+        float x2 = (x * x);
+        /* Taylor expansion out to x**11/11! factored into multiply-adds */
+        return x * (x2 * (x2 * (x2 * (x2 * ((x2 * (-IF11)) + IF9) - IF7) + IF5) - IF3) + 1);
+    }
+
+    @Override
+    public float render() {
+        // Convert raw sawtooth to sine.
+        float phase = incrementWrapPhase();
+        return fastSin(phase) * getAmplitude();
+    }
+
+}
diff --git a/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/synth/SineVoice.java b/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/synth/SineVoice.java
new file mode 100644
index 0000000..e80d2c7
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/synth/SineVoice.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2015 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.android.common.midi.synth;
+
+/**
+ * Replace sawtooth with a sine wave.
+ */
+public class SineVoice extends SawVoice {
+    @Override
+    protected SawOscillator createOscillator() {
+        return new SineOscillator();
+    }
+}
diff --git a/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/synth/SynthEngine.java b/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/synth/SynthEngine.java
new file mode 100644
index 0000000..6cd02a6
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/synth/SynthEngine.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2015 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.android.common.midi.synth;
+
+import android.media.midi.MidiReceiver;
+import android.util.Log;
+
+import com.example.android.common.midi.MidiConstants;
+import com.example.android.common.midi.MidiEventScheduler;
+import com.example.android.common.midi.MidiEventScheduler.MidiEvent;
+import com.example.android.common.midi.MidiFramer;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.Iterator;
+
+/**
+ * Very simple polyphonic, single channel synthesizer. It runs a background
+ * thread that processes MIDI events and synthesizes audio.
+ */
+public class SynthEngine extends MidiReceiver {
+
+    private static final String TAG = "SynthEngine";
+
+    public static final int FRAME_RATE = 48000;
+    private static final int FRAMES_PER_BUFFER = 240;
+    private static final int SAMPLES_PER_FRAME = 2;
+
+    private boolean go;
+    private Thread mThread;
+    private float[] mBuffer = new float[FRAMES_PER_BUFFER * SAMPLES_PER_FRAME];
+    private float mFrequencyScaler = 1.0f;
+    private float mBendRange = 2.0f; // semitones
+    private int mProgram;
+
+    private ArrayList<SynthVoice> mFreeVoices = new ArrayList<SynthVoice>();
+    private Hashtable<Integer, SynthVoice>
+            mVoices = new Hashtable<Integer, SynthVoice>();
+    private MidiEventScheduler mEventScheduler;
+    private MidiFramer mFramer;
+    private MidiReceiver mReceiver = new MyReceiver();
+    private SimpleAudioOutput mAudioOutput;
+
+    public SynthEngine() {
+        this(new SimpleAudioOutput());
+    }
+
+    public SynthEngine(SimpleAudioOutput audioOutput) {
+        mReceiver = new MyReceiver();
+        mFramer = new MidiFramer(mReceiver);
+        mAudioOutput = audioOutput;
+    }
+
+    @Override
+    public void onSend(byte[] data, int offset, int count, long timestamp)
+            throws IOException {
+        if (mEventScheduler != null) {
+            if (!MidiConstants.isAllActiveSensing(data, offset, count)) {
+                mEventScheduler.getReceiver().send(data, offset, count,
+                        timestamp);
+            }
+        }
+    }
+
+    private class MyReceiver extends MidiReceiver {
+        @Override
+        public void onSend(byte[] data, int offset, int count, long timestamp)
+                throws IOException {
+            byte command = (byte) (data[0] & MidiConstants.STATUS_COMMAND_MASK);
+            int channel = (byte) (data[0] & MidiConstants.STATUS_CHANNEL_MASK);
+            switch (command) {
+            case MidiConstants.STATUS_NOTE_OFF:
+                noteOff(channel, data[1], data[2]);
+                break;
+            case MidiConstants.STATUS_NOTE_ON:
+                noteOn(channel, data[1], data[2]);
+                break;
+            case MidiConstants.STATUS_PITCH_BEND:
+                int bend = (data[2] << 7) + data[1];
+                pitchBend(channel, bend);
+                break;
+            case MidiConstants.STATUS_PROGRAM_CHANGE:
+                mProgram = data[1];
+                mFreeVoices.clear();
+                break;
+            default:
+                logMidiMessage(data, offset, count);
+                break;
+            }
+        }
+    }
+
+    class MyRunnable implements Runnable {
+        @Override
+        public void run() {
+            try {
+                mAudioOutput.start(FRAME_RATE);
+                onLoopStarted();
+                while (go) {
+                    processMidiEvents();
+                    generateBuffer();
+                    mAudioOutput.write(mBuffer, 0, mBuffer.length);
+                    onBufferCompleted(FRAMES_PER_BUFFER);
+                }
+            } catch (Exception e) {
+                Log.e(TAG, "SynthEngine background thread exception.", e);
+            } finally {
+                onLoopEnded();
+                mAudioOutput.stop();
+            }
+        }
+    }
+
+    /**
+     * This is called form the synthesis thread before it starts looping.
+     */
+    public void onLoopStarted() {
+    }
+
+    /**
+     * This is called once at the end of each synthesis loop.
+     *
+     * @param framesPerBuffer
+     */
+    public void onBufferCompleted(int framesPerBuffer) {
+    }
+
+    /**
+     * This is called form the synthesis thread when it stop looping.
+     */
+    public void onLoopEnded() {
+    }
+
+    /**
+     * Assume message has been aligned to the start of a MIDI message.
+     *
+     * @param data
+     * @param offset
+     * @param count
+     */
+    public void logMidiMessage(byte[] data, int offset, int count) {
+        String text = "Received: ";
+        for (int i = 0; i < count; i++) {
+            text += String.format("0x%02X, ", data[offset + i]);
+        }
+        Log.i(TAG, text);
+    }
+
+    /**
+     * @throws IOException
+     *
+     */
+    private void processMidiEvents() throws IOException {
+        long now = System.nanoTime(); // TODO use audio presentation time
+        MidiEvent event = (MidiEvent) mEventScheduler.getNextEvent(now);
+        while (event != null) {
+            mFramer.send(event.data, 0, event.count, event.getTimestamp());
+            mEventScheduler.addEventToPool(event);
+            event = (MidiEvent) mEventScheduler.getNextEvent(now);
+        }
+    }
+
+    /**
+     *
+     */
+    private void generateBuffer() {
+        for (int i = 0; i < mBuffer.length; i++) {
+            mBuffer[i] = 0.0f;
+        }
+        Iterator<SynthVoice> iterator = mVoices.values().iterator();
+        while (iterator.hasNext()) {
+            SynthVoice voice = iterator.next();
+            if (voice.isDone()) {
+                iterator.remove();
+                // mFreeVoices.add(voice);
+            } else {
+                voice.mix(mBuffer, SAMPLES_PER_FRAME, 0.25f);
+            }
+        }
+    }
+
+    public void noteOff(int channel, int noteIndex, int velocity) {
+        SynthVoice voice = mVoices.get(noteIndex);
+        if (voice != null) {
+            voice.noteOff();
+        }
+    }
+
+    public void allNotesOff() {
+        Iterator<SynthVoice> iterator = mVoices.values().iterator();
+        while (iterator.hasNext()) {
+            SynthVoice voice = iterator.next();
+            voice.noteOff();
+        }
+    }
+
+    /**
+     * Create a SynthVoice.
+     */
+    public SynthVoice createVoice(int program) {
+        // For every odd program number use a sine wave.
+        if ((program & 1) == 1) {
+            return new SineVoice();
+        } else {
+            return new SawVoice();
+        }
+    }
+
+    /**
+     *
+     * @param channel
+     * @param noteIndex
+     * @param velocity
+     */
+    public void noteOn(int channel, int noteIndex, int velocity) {
+        if (velocity == 0) {
+            noteOff(channel, noteIndex, velocity);
+        } else {
+            mVoices.remove(noteIndex);
+            SynthVoice voice;
+            if (mFreeVoices.size() > 0) {
+                voice = mFreeVoices.remove(mFreeVoices.size() - 1);
+            } else {
+                voice = createVoice(mProgram);
+            }
+            voice.setFrequencyScaler(mFrequencyScaler);
+            voice.noteOn(noteIndex, velocity);
+            mVoices.put(noteIndex, voice);
+        }
+    }
+
+    public void pitchBend(int channel, int bend) {
+        double semitones = (mBendRange * (bend - 0x2000)) / 0x2000;
+        mFrequencyScaler = (float) Math.pow(2.0, semitones / 12.0);
+        Iterator<SynthVoice> iterator = mVoices.values().iterator();
+        while (iterator.hasNext()) {
+            SynthVoice voice = iterator.next();
+            voice.setFrequencyScaler(mFrequencyScaler);
+        }
+    }
+
+    /**
+     * Start the synthesizer.
+     */
+    public void start() {
+        stop();
+        go = true;
+        mThread = new Thread(new MyRunnable());
+        mEventScheduler = new MidiEventScheduler();
+        mThread.start();
+    }
+
+    /**
+     * Stop the synthesizer.
+     */
+    public void stop() {
+        go = false;
+        if (mThread != null) {
+            try {
+                mThread.interrupt();
+                mThread.join(500);
+            } catch (InterruptedException e) {
+                // OK, just stopping safely.
+            }
+            mThread = null;
+            mEventScheduler = null;
+        }
+    }
+}
diff --git a/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/synth/SynthUnit.java b/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/synth/SynthUnit.java
new file mode 100644
index 0000000..90599e2
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/synth/SynthUnit.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2015 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.android.common.midi.synth;
+
+public abstract class SynthUnit {
+
+    private static final double CONCERT_A_PITCH = 69.0;
+    private static final double CONCERT_A_FREQUENCY = 440.0;
+
+    /**
+     * @param pitch
+     *            MIDI pitch in semitones
+     * @return frequency
+     */
+    public static double pitchToFrequency(double pitch) {
+        double semitones = pitch - CONCERT_A_PITCH;
+        return CONCERT_A_FREQUENCY * Math.pow(2.0, semitones / 12.0);
+    }
+
+    public abstract float render();
+}
diff --git a/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/synth/SynthVoice.java b/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/synth/SynthVoice.java
new file mode 100644
index 0000000..78ba09a
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/common/midi/synth/SynthVoice.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2015 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.android.common.midi.synth;
+
+/**
+ * Base class for a polyphonic synthesizer voice.
+ */
+public abstract class SynthVoice {
+    private int mNoteIndex;
+    private float mAmplitude;
+    public static final int STATE_OFF = 0;
+    public static final int STATE_ON = 1;
+    private int mState = STATE_OFF;
+
+    public SynthVoice() {
+        mNoteIndex = -1;
+    }
+
+    public void noteOn(int noteIndex, int velocity) {
+        mState = STATE_ON;
+        this.mNoteIndex = noteIndex;
+        setAmplitude(velocity / 128.0f);
+    }
+
+    public void noteOff() {
+        mState = STATE_OFF;
+    }
+
+    /**
+     * Add the output of this voice to an output buffer.
+     *
+     * @param outputBuffer
+     * @param samplesPerFrame
+     * @param level
+     */
+    public void mix(float[] outputBuffer, int samplesPerFrame, float level) {
+        int numFrames = outputBuffer.length / samplesPerFrame;
+        for (int i = 0; i < numFrames; i++) {
+            float output = render();
+            int offset = i * samplesPerFrame;
+            for (int jf = 0; jf < samplesPerFrame; jf++) {
+                outputBuffer[offset + jf] += output * level;
+            }
+        }
+    }
+
+    public abstract float render();
+
+    public boolean isDone() {
+        return mState == STATE_OFF;
+    }
+
+    public int getNoteIndex() {
+        return mNoteIndex;
+    }
+
+    public float getAmplitude() {
+        return mAmplitude;
+    }
+
+    public void setAmplitude(float amplitude) {
+        this.mAmplitude = amplitude;
+    }
+
+    /**
+     * @param scaler
+     */
+    public void setFrequencyScaler(float scaler) {
+    }
+
+}
diff --git a/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/midisynth/MainActivity.java b/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/midisynth/MainActivity.java
new file mode 100644
index 0000000..92964b4
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/midisynth/MainActivity.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2015 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.android.midisynth;
+
+import android.app.ActionBar;
+import android.app.Activity;
+import android.content.pm.PackageManager;
+import android.media.midi.MidiDevice.MidiConnection;
+import android.media.midi.MidiDeviceInfo;
+import android.media.midi.MidiManager;
+import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.WindowManager;
+import android.widget.Toast;
+import android.widget.Toolbar;
+
+import com.example.android.common.midi.MidiOutputPortConnectionSelector;
+import com.example.android.common.midi.MidiPortConnector;
+import com.example.android.common.midi.MidiTools;
+
+/**
+ * Simple synthesizer as a MIDI Device.
+ */
+public class MainActivity extends Activity {
+    static final String TAG = "MidiSynthExample";
+
+    private MidiManager mMidiManager;
+    private MidiOutputPortConnectionSelector mPortSelector;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main);
+        setActionBar((Toolbar) findViewById(R.id.toolbar));
+        ActionBar actionBar = getActionBar();
+        if (actionBar != null) {
+            actionBar.setDisplayShowTitleEnabled(false);
+        }
+
+        if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_MIDI)) {
+            setupMidi();
+        }
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        getMenuInflater().inflate(R.menu.main, menu);
+        setKeepScreenOn(menu.findItem(R.id.action_keep_screen_on).isChecked());
+        return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case R.id.action_keep_screen_on:
+                boolean checked = !item.isChecked();
+                setKeepScreenOn(checked);
+                item.setChecked(checked);
+                break;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    private void setKeepScreenOn(boolean keepScreenOn) {
+        if (keepScreenOn) {
+            getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+        } else {
+            getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+        }
+    }
+
+    private void setupMidi() {
+        // Setup MIDI
+        mMidiManager = (MidiManager) getSystemService(MIDI_SERVICE);
+
+        MidiDeviceInfo synthInfo = MidiTools.findDevice(mMidiManager, "AndroidTest",
+                "SynthExample");
+        int portIndex = 0;
+        mPortSelector = new MidiOutputPortConnectionSelector(mMidiManager, this,
+                R.id.spinner_synth_sender, synthInfo, portIndex);
+        mPortSelector.setConnectedListener(new MyPortsConnectedListener());
+    }
+
+    private void closeSynthResources() {
+        if (mPortSelector != null) {
+            mPortSelector.close();
+        }
+    }
+
+    // TODO A better way would be to listen to the synth server
+    // for open/close events and then disable/enable the spinner.
+    private class MyPortsConnectedListener
+            implements MidiPortConnector.OnPortsConnectedListener {
+        @Override
+        public void onPortsConnected(final MidiConnection connection) {
+            runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    if (connection == null) {
+                        Toast.makeText(MainActivity.this,
+                                R.string.error_port_busy, Toast.LENGTH_SHORT)
+                                .show();
+                        mPortSelector.clearSelection();
+                    } else {
+                        Toast.makeText(MainActivity.this,
+                                R.string.port_open_ok, Toast.LENGTH_SHORT)
+                                .show();
+                    }
+                }
+            });
+        }
+    }
+
+    @Override
+    public void onDestroy() {
+        closeSynthResources();
+        super.onDestroy();
+    }
+
+}
diff --git a/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/midisynth/MidiSynthDeviceService.java b/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/midisynth/MidiSynthDeviceService.java
new file mode 100644
index 0000000..b9f25ee
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/Application/src/main/java/com/example/android/midisynth/MidiSynthDeviceService.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2015 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.android.midisynth;
+
+import android.media.midi.MidiDeviceService;
+import android.media.midi.MidiDeviceStatus;
+import android.media.midi.MidiReceiver;
+
+import com.example.android.common.midi.synth.SynthEngine;
+
+public class MidiSynthDeviceService extends MidiDeviceService {
+
+    private static final String TAG = MainActivity.TAG;
+    private SynthEngine mSynthEngine = new SynthEngine();
+    private boolean mSynthStarted = false;
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+    }
+
+    @Override
+    public void onDestroy() {
+        mSynthEngine.stop();
+        super.onDestroy();
+    }
+
+    @Override
+    public MidiReceiver[] onGetInputPortReceivers() {
+        return new MidiReceiver[]{mSynthEngine};
+    }
+
+    /**
+     * This will get called when clients connect or disconnect.
+     */
+    @Override
+    public void onDeviceStatusChanged(MidiDeviceStatus status) {
+        if (status.isInputPortOpen(0) && !mSynthStarted) {
+            mSynthEngine.start();
+            mSynthStarted = true;
+        } else if (!status.isInputPortOpen(0) && mSynthStarted) {
+            mSynthEngine.stop();
+            mSynthStarted = false;
+        }
+    }
+
+}
diff --git a/prebuilts/gradle/MidiSynth/Application/src/main/res/drawable-hdpi/tile.9.png b/prebuilts/gradle/MidiSynth/Application/src/main/res/drawable-hdpi/tile.9.png
new file mode 100644
index 0000000..1358628
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/Application/src/main/res/drawable-hdpi/tile.9.png
Binary files differ
diff --git a/prebuilts/gradle/MidiSynth/Application/src/main/res/layout/main.xml b/prebuilts/gradle/MidiSynth/Application/src/main/res/layout/main.xml
new file mode 100644
index 0000000..6b9e2c7
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/Application/src/main/res/layout/main.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2015 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_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <Toolbar
+        android:id="@+id/toolbar"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:background="?android:attr/colorPrimary"
+        android:elevation="4dp"
+        android:minHeight="?android:attr/actionBarSize"
+        android:popupTheme="@android:style/ThemeOverlay.Material.Light"
+        android:theme="@android:style/ThemeOverlay.Material.Dark.ActionBar">
+
+        <Spinner
+            android:id="@+id/spinner_synth_sender"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:entries="@array/senders"
+            android:popupTheme="@android:style/ThemeOverlay.Material.Light" />
+
+    </Toolbar>
+
+    <TextView
+        android:id="@+id/message"
+        style="@android:style/TextAppearance.Medium"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1"
+        android:paddingBottom="8dp"
+        android:paddingEnd="16dp"
+        android:paddingStart="16dp"
+        android:paddingTop="8dp"
+        android:text="@string/synth_sender_text" />
+
+</LinearLayout>
diff --git a/prebuilts/gradle/MidiSynth/Application/src/main/res/menu/main.xml b/prebuilts/gradle/MidiSynth/Application/src/main/res/menu/main.xml
new file mode 100644
index 0000000..33093f0
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/Application/src/main/res/menu/main.xml
@@ -0,0 +1,25 @@
+<!--
+ Copyright 2015 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.
+-->
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item
+        android:id="@+id/action_keep_screen_on"
+        android:checkable="true"
+        android:checked="true"
+        android:showAsAction="never"
+        android:title="@string/keep_screen_on"/>
+
+</menu>
diff --git a/prebuilts/gradle/MidiSynth/Application/src/main/res/mipmap-hdpi/ic_launcher.png b/prebuilts/gradle/MidiSynth/Application/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..38250e7
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/Application/src/main/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/prebuilts/gradle/MidiSynth/Application/src/main/res/mipmap-mdpi/ic_launcher.png b/prebuilts/gradle/MidiSynth/Application/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..58c0025
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/Application/src/main/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/prebuilts/gradle/MidiSynth/Application/src/main/res/mipmap-xhdpi/ic_launcher.png b/prebuilts/gradle/MidiSynth/Application/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..3694855
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/Application/src/main/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/prebuilts/gradle/MidiSynth/Application/src/main/res/mipmap-xxhdpi/ic_launcher.png b/prebuilts/gradle/MidiSynth/Application/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..fb50ad1
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/Application/src/main/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/prebuilts/gradle/MidiSynth/Application/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/prebuilts/gradle/MidiSynth/Application/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..801bf9c
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/Application/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/prebuilts/gradle/MidiSynth/Application/src/main/res/values-sw600dp/template-dimens.xml b/prebuilts/gradle/MidiSynth/Application/src/main/res/values-sw600dp/template-dimens.xml
new file mode 100644
index 0000000..22074a2
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/Application/src/main/res/values-sw600dp/template-dimens.xml
@@ -0,0 +1,24 @@
+<!--
+  Copyright 2013 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>
+
+    <!-- Semantic definitions -->
+
+    <dimen name="horizontal_page_margin">@dimen/margin_huge</dimen>
+    <dimen name="vertical_page_margin">@dimen/margin_medium</dimen>
+
+</resources>
diff --git a/prebuilts/gradle/MidiSynth/Application/src/main/res/values-sw600dp/template-styles.xml b/prebuilts/gradle/MidiSynth/Application/src/main/res/values-sw600dp/template-styles.xml
new file mode 100644
index 0000000..03d1974
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/Application/src/main/res/values-sw600dp/template-styles.xml
@@ -0,0 +1,25 @@
+<!--
+  Copyright 2013 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="Widget.SampleMessage">
+        <item name="android:textAppearance">?android:textAppearanceLarge</item>
+        <item name="android:lineSpacingMultiplier">1.2</item>
+        <item name="android:shadowDy">-6.5</item>
+    </style>
+
+</resources>
diff --git a/prebuilts/gradle/MidiSynth/Application/src/main/res/values-v11/template-styles.xml b/prebuilts/gradle/MidiSynth/Application/src/main/res/values-v11/template-styles.xml
new file mode 100644
index 0000000..8c1ea66
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/Application/src/main/res/values-v11/template-styles.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright 2013 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>
+
+    <!-- Activity themes -->
+    <style name="Theme.Base" parent="android:Theme.Holo.Light" />
+
+</resources>
diff --git a/prebuilts/gradle/MidiSynth/Application/src/main/res/values-v21/base-colors.xml b/prebuilts/gradle/MidiSynth/Application/src/main/res/values-v21/base-colors.xml
new file mode 100644
index 0000000..8b6ec3f
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/Application/src/main/res/values-v21/base-colors.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2013 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>
+
+
+</resources>
diff --git a/prebuilts/gradle/MidiSynth/Application/src/main/res/values-v21/base-template-styles.xml b/prebuilts/gradle/MidiSynth/Application/src/main/res/values-v21/base-template-styles.xml
new file mode 100644
index 0000000..c778e4f
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/Application/src/main/res/values-v21/base-template-styles.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2013 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>
+
+    <!-- Activity themes -->
+    <style name="Theme.Base" parent="android:Theme.Material.Light">
+    </style>
+
+</resources>
diff --git a/prebuilts/gradle/MidiSynth/Application/src/main/res/values/base-strings.xml b/prebuilts/gradle/MidiSynth/Application/src/main/res/values/base-strings.xml
new file mode 100644
index 0000000..d38815f
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/Application/src/main/res/values/base-strings.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2013 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>
+    <string name="app_name">MidiSynth</string>
+    <string name="intro_message">
+        <![CDATA[
+        
+
+This sample demonstrates how to use the MIDI API to receive and play MIDI messages coming from an
+attached input device.
+
+        
+        ]]>
+    </string>
+</resources>
diff --git a/prebuilts/gradle/MidiSynth/Application/src/main/res/values/colors.xml b/prebuilts/gradle/MidiSynth/Application/src/main/res/values/colors.xml
new file mode 100644
index 0000000..4d2204f
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/Application/src/main/res/values/colors.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2015 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="primary">#4CAF50</color>
+    <color name="primary_dark">#388E3C</color>
+    <color name="primary_light">#C8E6C9</color>
+    <color name="accent">#FFEB3B</color>
+    <color name="primary_text">#212121</color>
+    <color name="secondary_text">#727272</color>
+    <color name="icons">#FFFFFF</color>
+    <color name="divider">#B6B6B6</color>
+</resources>
diff --git a/prebuilts/gradle/MidiSynth/Application/src/main/res/values/strings.xml b/prebuilts/gradle/MidiSynth/Application/src/main/res/values/strings.xml
new file mode 100644
index 0000000..76a8fa2
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/Application/src/main/res/values/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2015 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>
+    <string name="synth_sender_text">Select Sender for Synth</string>
+    <string name="error_port_busy">Selected port is in use or unavailable.</string>
+    <string name="port_open_ok">Port opened OK.</string>
+    <string-array name="senders">
+        <item>"none"</item>
+    </string-array>
+    <string name="keep_screen_on">Keep screen on</string>
+</resources>
diff --git a/prebuilts/gradle/MidiSynth/Application/src/main/res/values/styles.xml b/prebuilts/gradle/MidiSynth/Application/src/main/res/values/styles.xml
new file mode 100644
index 0000000..30d6455
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/Application/src/main/res/values/styles.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2015 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="MidiSynthTheme" parent="android:Theme.Material.Light.NoActionBar">
+        <item name="android:colorPrimary">@color/primary</item>
+        <item name="android:colorPrimaryDark">@color/primary_dark</item>
+        <item name="android:colorAccent">@color/accent</item>
+    </style>
+
+</resources>
diff --git a/prebuilts/gradle/MidiSynth/Application/src/main/res/values/template-dimens.xml b/prebuilts/gradle/MidiSynth/Application/src/main/res/values/template-dimens.xml
new file mode 100644
index 0000000..39e710b
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/Application/src/main/res/values/template-dimens.xml
@@ -0,0 +1,32 @@
+<!--
+  Copyright 2013 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>
+
+    <!-- Define standard dimensions to comply with Holo-style grids and rhythm. -->
+
+    <dimen name="margin_tiny">4dp</dimen>
+    <dimen name="margin_small">8dp</dimen>
+    <dimen name="margin_medium">16dp</dimen>
+    <dimen name="margin_large">32dp</dimen>
+    <dimen name="margin_huge">64dp</dimen>
+
+    <!-- Semantic definitions -->
+
+    <dimen name="horizontal_page_margin">@dimen/margin_medium</dimen>
+    <dimen name="vertical_page_margin">@dimen/margin_medium</dimen>
+
+</resources>
diff --git a/prebuilts/gradle/MidiSynth/Application/src/main/res/values/template-styles.xml b/prebuilts/gradle/MidiSynth/Application/src/main/res/values/template-styles.xml
new file mode 100644
index 0000000..6e7d593
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/Application/src/main/res/values/template-styles.xml
@@ -0,0 +1,42 @@
+<!--
+  Copyright 2013 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>
+
+    <!-- Activity themes -->
+
+    <style name="Theme.Base" parent="android:Theme.Light" />
+
+    <style name="Theme.Sample" parent="Theme.Base" />
+
+    <style name="AppTheme" parent="Theme.Sample" />
+    <!-- Widget styling -->
+
+    <style name="Widget" />
+
+    <style name="Widget.SampleMessage">
+        <item name="android:textAppearance">?android:textAppearanceMedium</item>
+        <item name="android:lineSpacingMultiplier">1.1</item>
+    </style>
+
+    <style name="Widget.SampleMessageTile">
+        <item name="android:background">@drawable/tile</item>
+        <item name="android:shadowColor">#7F000000</item>
+        <item name="android:shadowDy">-3.5</item>
+        <item name="android:shadowRadius">2</item>
+    </style>
+
+</resources>
diff --git a/prebuilts/gradle/MidiSynth/Application/src/main/res/xml/synth_device_info.xml b/prebuilts/gradle/MidiSynth/Application/src/main/res/xml/synth_device_info.xml
new file mode 100644
index 0000000..405e87e
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/Application/src/main/res/xml/synth_device_info.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2015 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.
+-->
+<devices>
+    <device manufacturer="AndroidTest" product="SynthExample">
+        <input-port name="input" />
+    </device>
+</devices>
diff --git a/prebuilts/gradle/MidiSynth/CONTRIBUTING.md b/prebuilts/gradle/MidiSynth/CONTRIBUTING.md
new file mode 100644
index 0000000..faa8b5c
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/CONTRIBUTING.md
@@ -0,0 +1,35 @@
+# How to become a contributor and submit your own code
+
+## Contributor License Agreements
+
+We'd love to accept your sample apps and patches! Before we can take them, we
+have to jump a couple of legal hurdles.
+
+Please fill out either the individual or corporate Contributor License Agreement (CLA).
+
+  * If you are an individual writing original source code and you're sure you
+    own the intellectual property, then you'll need to sign an [individual CLA]
+    (https://cla.developers.google.com).
+  * If you work for a company that wants to allow you to contribute your work,
+    then you'll need to sign a [corporate CLA]
+    (https://cla.developers.google.com).
+
+Follow either of the two links above to access the appropriate CLA and
+instructions for how to sign and return it. Once we receive it, we'll be able to
+accept your pull requests.
+
+## Contributing A Patch
+
+1. Submit an issue describing your proposed change to the repo in question.
+1. The repo owner will respond to your issue promptly.
+1. If your proposed change is accepted, and you haven't already done so, sign a
+   Contributor License Agreement (see details above).
+1. Fork the desired repo, develop and test your code changes.
+1. Ensure that your code adheres to the existing style in the sample to which
+   you are contributing. Refer to the
+   [Android Code Style Guide]
+   (https://source.android.com/source/code-style.html) for the
+   recommended coding standards for this organization.
+1. Ensure that your code has an appropriate set of unit tests which all pass.
+1. Submit a pull request.
+
diff --git a/prebuilts/gradle/MidiSynth/LICENSE b/prebuilts/gradle/MidiSynth/LICENSE
new file mode 100644
index 0000000..4f22946
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/LICENSE
@@ -0,0 +1,647 @@
+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
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "{}"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright {yyyy} {name of copyright owner}
+
+   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.
+
+All image and audio files (including *.png, *.jpg, *.svg, *.mp3, *.wav 
+and *.ogg) are licensed under the CC-BY-NC license. All other files are 
+licensed under the Apache 2 license.
+
+CC-BY-NC License
+----------------
+
+Attribution-NonCommercial-ShareAlike 4.0 International
+
+=======================================================================
+
+Creative Commons Corporation ("Creative Commons") is not a law firm and
+does not provide legal services or legal advice. Distribution of
+Creative Commons public licenses does not create a lawyer-client or
+other relationship. Creative Commons makes its licenses and related
+information available on an "as-is" basis. Creative Commons gives no
+warranties regarding its licenses, any material licensed under their
+terms and conditions, or any related information. Creative Commons
+disclaims all liability for damages resulting from their use to the
+fullest extent possible.
+
+Using Creative Commons Public Licenses
+
+Creative Commons public licenses provide a standard set of terms and
+conditions that creators and other rights holders may use to share
+original works of authorship and other material subject to copyright
+and certain other rights specified in the public license below. The
+following considerations are for informational purposes only, are not
+exhaustive, and do not form part of our licenses.
+
+     Considerations for licensors: Our public licenses are
+     intended for use by those authorized to give the public
+     permission to use material in ways otherwise restricted by
+     copyright and certain other rights. Our licenses are
+     irrevocable. Licensors should read and understand the terms
+     and conditions of the license they choose before applying it.
+     Licensors should also secure all rights necessary before
+     applying our licenses so that the public can reuse the
+     material as expected. Licensors should clearly mark any
+     material not subject to the license. This includes other CC-
+     licensed material, or material used under an exception or
+     limitation to copyright. More considerations for licensors:
+	wiki.creativecommons.org/Considerations_for_licensors
+
+     Considerations for the public: By using one of our public
+     licenses, a licensor grants the public permission to use the
+     licensed material under specified terms and conditions. If
+     the licensor's permission is not necessary for any reason--for
+     example, because of any applicable exception or limitation to
+     copyright--then that use is not regulated by the license. Our
+     licenses grant only permissions under copyright and certain
+     other rights that a licensor has authority to grant. Use of
+     the licensed material may still be restricted for other
+     reasons, including because others have copyright or other
+     rights in the material. A licensor may make special requests,
+     such as asking that all changes be marked or described.
+     Although not required by our licenses, you are encouraged to
+     respect those requests where reasonable. More_considerations
+     for the public: 
+	wiki.creativecommons.org/Considerations_for_licensees
+
+=======================================================================
+
+Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International
+Public License
+
+By exercising the Licensed Rights (defined below), You accept and agree
+to be bound by the terms and conditions of this Creative Commons
+Attribution-NonCommercial-ShareAlike 4.0 International Public License
+("Public License"). To the extent this Public License may be
+interpreted as a contract, You are granted the Licensed Rights in
+consideration of Your acceptance of these terms and conditions, and the
+Licensor grants You such rights in consideration of benefits the
+Licensor receives from making the Licensed Material available under
+these terms and conditions.
+
+
+Section 1 -- Definitions.
+
+  a. Adapted Material means material subject to Copyright and Similar
+     Rights that is derived from or based upon the Licensed Material
+     and in which the Licensed Material is translated, altered,
+     arranged, transformed, or otherwise modified in a manner requiring
+     permission under the Copyright and Similar Rights held by the
+     Licensor. For purposes of this Public License, where the Licensed
+     Material is a musical work, performance, or sound recording,
+     Adapted Material is always produced where the Licensed Material is
+     synched in timed relation with a moving image.
+
+  b. Adapter's License means the license You apply to Your Copyright
+     and Similar Rights in Your contributions to Adapted Material in
+     accordance with the terms and conditions of this Public License.
+
+  c. BY-NC-SA Compatible License means a license listed at
+     creativecommons.org/compatiblelicenses, approved by Creative
+     Commons as essentially the equivalent of this Public License.
+
+  d. Copyright and Similar Rights means copyright and/or similar rights
+     closely related to copyright including, without limitation,
+     performance, broadcast, sound recording, and Sui Generis Database
+     Rights, without regard to how the rights are labeled or
+     categorized. For purposes of this Public License, the rights
+     specified in Section 2(b)(1)-(2) are not Copyright and Similar
+     Rights.
+
+  e. Effective Technological Measures means those measures that, in the
+     absence of proper authority, may not be circumvented under laws
+     fulfilling obligations under Article 11 of the WIPO Copyright
+     Treaty adopted on December 20, 1996, and/or similar international
+     agreements.
+
+  f. Exceptions and Limitations means fair use, fair dealing, and/or
+     any other exception or limitation to Copyright and Similar Rights
+     that applies to Your use of the Licensed Material.
+
+  g. License Elements means the license attributes listed in the name
+     of a Creative Commons Public License. The License Elements of this
+     Public License are Attribution, NonCommercial, and ShareAlike.
+
+  h. Licensed Material means the artistic or literary work, database,
+     or other material to which the Licensor applied this Public
+     License.
+
+  i. Licensed Rights means the rights granted to You subject to the
+     terms and conditions of this Public License, which are limited to
+     all Copyright and Similar Rights that apply to Your use of the
+     Licensed Material and that the Licensor has authority to license.
+
+  j. Licensor means the individual(s) or entity(ies) granting rights
+     under this Public License.
+
+  k. NonCommercial means not primarily intended for or directed towards
+     commercial advantage or monetary compensation. For purposes of
+     this Public License, the exchange of the Licensed Material for
+     other material subject to Copyright and Similar Rights by digital
+     file-sharing or similar means is NonCommercial provided there is
+     no payment of monetary compensation in connection with the
+     exchange.
+
+  l. Share means to provide material to the public by any means or
+     process that requires permission under the Licensed Rights, such
+     as reproduction, public display, public performance, distribution,
+     dissemination, communication, or importation, and to make material
+     available to the public including in ways that members of the
+     public may access the material from a place and at a time
+     individually chosen by them.
+
+  m. Sui Generis Database Rights means rights other than copyright
+     resulting from Directive 96/9/EC of the European Parliament and of
+     the Council of 11 March 1996 on the legal protection of databases,
+     as amended and/or succeeded, as well as other essentially
+     equivalent rights anywhere in the world.
+
+  n. You means the individual or entity exercising the Licensed Rights
+     under this Public License. Your has a corresponding meaning.
+
+
+Section 2 -- Scope.
+
+  a. License grant.
+
+       1. Subject to the terms and conditions of this Public License,
+          the Licensor hereby grants You a worldwide, royalty-free,
+          non-sublicensable, non-exclusive, irrevocable license to
+          exercise the Licensed Rights in the Licensed Material to:
+
+            a. reproduce and Share the Licensed Material, in whole or
+               in part, for NonCommercial purposes only; and
+
+            b. produce, reproduce, and Share Adapted Material for
+               NonCommercial purposes only.
+
+       2. Exceptions and Limitations. For the avoidance of doubt, where
+          Exceptions and Limitations apply to Your use, this Public
+          License does not apply, and You do not need to comply with
+          its terms and conditions.
+
+       3. Term. The term of this Public License is specified in Section
+          6(a).
+
+       4. Media and formats; technical modifications allowed. The
+          Licensor authorizes You to exercise the Licensed Rights in
+          all media and formats whether now known or hereafter created,
+          and to make technical modifications necessary to do so. The
+          Licensor waives and/or agrees not to assert any right or
+          authority to forbid You from making technical modifications
+          necessary to exercise the Licensed Rights, including
+          technical modifications necessary to circumvent Effective
+          Technological Measures. For purposes of this Public License,
+          simply making modifications authorized by this Section 2(a)
+          (4) never produces Adapted Material.
+
+       5. Downstream recipients.
+
+            a. Offer from the Licensor -- Licensed Material. Every
+               recipient of the Licensed Material automatically
+               receives an offer from the Licensor to exercise the
+               Licensed Rights under the terms and conditions of this
+               Public License.
+
+            b. Additional offer from the Licensor -- Adapted Material.
+               Every recipient of Adapted Material from You
+               automatically receives an offer from the Licensor to
+               exercise the Licensed Rights in the Adapted Material
+               under the conditions of the Adapter's License You apply.
+
+            c. No downstream restrictions. You may not offer or impose
+               any additional or different terms or conditions on, or
+               apply any Effective Technological Measures to, the
+               Licensed Material if doing so restricts exercise of the
+               Licensed Rights by any recipient of the Licensed
+               Material.
+
+       6. No endorsement. Nothing in this Public License constitutes or
+          may be construed as permission to assert or imply that You
+          are, or that Your use of the Licensed Material is, connected
+          with, or sponsored, endorsed, or granted official status by,
+          the Licensor or others designated to receive attribution as
+          provided in Section 3(a)(1)(A)(i).
+
+  b. Other rights.
+
+       1. Moral rights, such as the right of integrity, are not
+          licensed under this Public License, nor are publicity,
+          privacy, and/or other similar personality rights; however, to
+          the extent possible, the Licensor waives and/or agrees not to
+          assert any such rights held by the Licensor to the limited
+          extent necessary to allow You to exercise the Licensed
+          Rights, but not otherwise.
+
+       2. Patent and trademark rights are not licensed under this
+          Public License.
+
+       3. To the extent possible, the Licensor waives any right to
+          collect royalties from You for the exercise of the Licensed
+          Rights, whether directly or through a collecting society
+          under any voluntary or waivable statutory or compulsory
+          licensing scheme. In all other cases the Licensor expressly
+          reserves any right to collect such royalties, including when
+          the Licensed Material is used other than for NonCommercial
+          purposes.
+
+
+Section 3 -- License Conditions.
+
+Your exercise of the Licensed Rights is expressly made subject to the
+following conditions.
+
+  a. Attribution.
+
+       1. If You Share the Licensed Material (including in modified
+          form), You must:
+
+            a. retain the following if it is supplied by the Licensor
+               with the Licensed Material:
+
+                 i. identification of the creator(s) of the Licensed
+                    Material and any others designated to receive
+                    attribution, in any reasonable manner requested by
+                    the Licensor (including by pseudonym if
+                    designated);
+
+                ii. a copyright notice;
+
+               iii. a notice that refers to this Public License;
+
+                iv. a notice that refers to the disclaimer of
+                    warranties;
+
+                 v. a URI or hyperlink to the Licensed Material to the
+                    extent reasonably practicable;
+
+            b. indicate if You modified the Licensed Material and
+               retain an indication of any previous modifications; and
+
+            c. indicate the Licensed Material is licensed under this
+               Public License, and include the text of, or the URI or
+               hyperlink to, this Public License.
+
+       2. You may satisfy the conditions in Section 3(a)(1) in any
+          reasonable manner based on the medium, means, and context in
+          which You Share the Licensed Material. For example, it may be
+          reasonable to satisfy the conditions by providing a URI or
+          hyperlink to a resource that includes the required
+          information.
+       3. If requested by the Licensor, You must remove any of the
+          information required by Section 3(a)(1)(A) to the extent
+          reasonably practicable.
+
+  b. ShareAlike.
+
+     In addition to the conditions in Section 3(a), if You Share
+     Adapted Material You produce, the following conditions also apply.
+
+       1. The Adapter's License You apply must be a Creative Commons
+          license with the same License Elements, this version or
+          later, or a BY-NC-SA Compatible License.
+
+       2. You must include the text of, or the URI or hyperlink to, the
+          Adapter's License You apply. You may satisfy this condition
+          in any reasonable manner based on the medium, means, and
+          context in which You Share Adapted Material.
+
+       3. You may not offer or impose any additional or different terms
+          or conditions on, or apply any Effective Technological
+          Measures to, Adapted Material that restrict exercise of the
+          rights granted under the Adapter's License You apply.
+
+
+Section 4 -- Sui Generis Database Rights.
+
+Where the Licensed Rights include Sui Generis Database Rights that
+apply to Your use of the Licensed Material:
+
+  a. for the avoidance of doubt, Section 2(a)(1) grants You the right
+     to extract, reuse, reproduce, and Share all or a substantial
+     portion of the contents of the database for NonCommercial purposes
+     only;
+
+  b. if You include all or a substantial portion of the database
+     contents in a database in which You have Sui Generis Database
+     Rights, then the database in which You have Sui Generis Database
+     Rights (but not its individual contents) is Adapted Material,
+     including for purposes of Section 3(b); and
+
+  c. You must comply with the conditions in Section 3(a) if You Share
+     all or a substantial portion of the contents of the database.
+
+For the avoidance of doubt, this Section 4 supplements and does not
+replace Your obligations under this Public License where the Licensed
+Rights include other Copyright and Similar Rights.
+
+
+Section 5 -- Disclaimer of Warranties and Limitation of Liability.
+
+  a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
+     EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
+     AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
+     ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
+     IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
+     WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
+     PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
+     ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
+     KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
+     ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
+
+  b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
+     TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
+     NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
+     INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
+     COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
+     USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
+     ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
+     DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
+     IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
+
+  c. The disclaimer of warranties and limitation of liability provided
+     above shall be interpreted in a manner that, to the extent
+     possible, most closely approximates an absolute disclaimer and
+     waiver of all liability.
+
+
+Section 6 -- Term and Termination.
+
+  a. This Public License applies for the term of the Copyright and
+     Similar Rights licensed here. However, if You fail to comply with
+     this Public License, then Your rights under this Public License
+     terminate automatically.
+
+  b. Where Your right to use the Licensed Material has terminated under
+     Section 6(a), it reinstates:
+
+       1. automatically as of the date the violation is cured, provided
+          it is cured within 30 days of Your discovery of the
+          violation; or
+
+       2. upon express reinstatement by the Licensor.
+
+     For the avoidance of doubt, this Section 6(b) does not affect any
+     right the Licensor may have to seek remedies for Your violations
+     of this Public License.
+
+  c. For the avoidance of doubt, the Licensor may also offer the
+     Licensed Material under separate terms or conditions or stop
+     distributing the Licensed Material at any time; however, doing so
+     will not terminate this Public License.
+
+  d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
+     License.
+
+
+Section 7 -- Other Terms and Conditions.
+
+  a. The Licensor shall not be bound by any additional or different
+     terms or conditions communicated by You unless expressly agreed.
+
+  b. Any arrangements, understandings, or agreements regarding the
+     Licensed Material not stated herein are separate from and
+     independent of the terms and conditions of this Public License.
+
+
+Section 8 -- Interpretation.
+
+  a. For the avoidance of doubt, this Public License does not, and
+     shall not be interpreted to, reduce, limit, restrict, or impose
+     conditions on any use of the Licensed Material that could lawfully
+     be made without permission under this Public License.
+
+  b. To the extent possible, if any provision of this Public License is
+     deemed unenforceable, it shall be automatically reformed to the
+     minimum extent necessary to make it enforceable. If the provision
+     cannot be reformed, it shall be severed from this Public License
+     without affecting the enforceability of the remaining terms and
+     conditions.
+
+  c. No term or condition of this Public License will be waived and no
+     failure to comply consented to unless expressly agreed to by the
+     Licensor.
+
+  d. Nothing in this Public License constitutes or may be interpreted
+     as a limitation upon, or waiver of, any privileges and immunities
+     that apply to the Licensor or You, including from the legal
+     processes of any jurisdiction or authority.
+
+=======================================================================
+
+Creative Commons is not a party to its public licenses.
+Notwithstanding, Creative Commons may elect to apply one of its public
+licenses to material it publishes and in those instances will be
+considered the "Licensor." Except for the limited purpose of indicating
+that material is shared under a Creative Commons public license or as
+otherwise permitted by the Creative Commons policies published at
+creativecommons.org/policies, Creative Commons does not authorize the
+use of the trademark "Creative Commons" or any other trademark or logo
+of Creative Commons without its prior written consent including,
+without limitation, in connection with any unauthorized modifications
+to any of its public licenses or any other arrangements,
+understandings, or agreements concerning use of licensed material. For
+the avoidance of doubt, this paragraph does not form part of the public
+licenses.
+
+Creative Commons may be contacted at creativecommons.org.
+
diff --git a/prebuilts/gradle/MidiSynth/README.md b/prebuilts/gradle/MidiSynth/README.md
new file mode 100644
index 0000000..e0d6733
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/README.md
@@ -0,0 +1,72 @@
+
+Android MidiSynth Sample
+===================================
+
+Sample demonstrating how to use the MIDI API to receive and play MIDI messages coming from an
+attached input device (MIDI keyboard).
+
+Introduction
+------------
+
+The Android MIDI API ([android.media.midi][1]) allows developers to connect a MIDI device to
+an Android device and process MIDI messages coming from it.
+
+This sample demonstrates some basic features of the MIDI API, such as:
+
+- Enumeration of currently available devices (including name, vendor, capabilities, etc)
+- Notification when MIDI devices are plugged in or unplugged
+- Receiving and processing MIDI messages
+
+This sample contains a simple implementation of an oscillator and note playback.
+
+[1]: https://developer.android.com/reference/android/media/midi/package-summary.html
+
+Pre-requisites
+--------------
+
+- Android SDK v23
+- Android Build Tools v23.0.0
+- Android Support Repository
+
+Screenshots
+-------------
+
+<img src="screenshots/1-main.png" height="400" alt="Screenshot"/> 
+
+Getting Started
+---------------
+
+This sample uses the Gradle build system. To build this project, use the
+"gradlew build" command or use "Import Project" in Android Studio.
+
+Support
+-------
+
+- Google+ Community: https://plus.google.com/communities/105153134372062985968
+- Stack Overflow: http://stackoverflow.com/questions/tagged/android
+
+If you've found an error in this sample, please file an issue:
+https://github.com/googlesamples/android-MidiSynth
+
+Patches are encouraged, and may be submitted by forking this project and
+submitting a pull request through GitHub. Please see CONTRIBUTING.md for more details.
+
+License
+-------
+
+Copyright 2014 The Android Open Source Project, Inc.
+
+Licensed to the Apache Software Foundation (ASF) under one or more contributor
+license agreements.  See the NOTICE file distributed with this work for
+additional information regarding copyright ownership.  The ASF licenses this
+file to you 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.
diff --git a/prebuilts/gradle/MidiSynth/build.gradle b/prebuilts/gradle/MidiSynth/build.gradle
new file mode 100644
index 0000000..1901ba9
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/build.gradle
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/prebuilts/gradle/MidiSynth/gradle/wrapper/gradle-wrapper.jar b/prebuilts/gradle/MidiSynth/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..8c0fb64
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/prebuilts/gradle/MidiSynth/gradle/wrapper/gradle-wrapper.properties b/prebuilts/gradle/MidiSynth/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..afb3296
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Wed Apr 10 15:27:10 PDT 2013
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=http\://services.gradle.org/distributions/gradle-2.2.1-bin.zip
diff --git a/prebuilts/gradle/MidiSynth/gradlew b/prebuilts/gradle/MidiSynth/gradlew
new file mode 100755
index 0000000..91a7e26
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+    echo "$*"
+}
+
+die ( ) {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+    [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=$((i+1))
+    done
+    case $i in
+        (0) set -- ;;
+        (1) set -- "$args0" ;;
+        (2) set -- "$args0" "$args1" ;;
+        (3) set -- "$args0" "$args1" "$args2" ;;
+        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+    JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/prebuilts/gradle/MidiSynth/gradlew.bat b/prebuilts/gradle/MidiSynth/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off

+@rem ##########################################################################

+@rem

+@rem  Gradle startup script for Windows

+@rem

+@rem ##########################################################################

+

+@rem Set local scope for the variables with windows NT shell

+if "%OS%"=="Windows_NT" setlocal

+

+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.

+set DEFAULT_JVM_OPTS=

+

+set DIRNAME=%~dp0

+if "%DIRNAME%" == "" set DIRNAME=.

+set APP_BASE_NAME=%~n0

+set APP_HOME=%DIRNAME%

+

+@rem Find java.exe

+if defined JAVA_HOME goto findJavaFromJavaHome

+

+set JAVA_EXE=java.exe

+%JAVA_EXE% -version >NUL 2>&1

+if "%ERRORLEVEL%" == "0" goto init

+

+echo.

+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.

+echo.

+echo Please set the JAVA_HOME variable in your environment to match the

+echo location of your Java installation.

+

+goto fail

+

+:findJavaFromJavaHome

+set JAVA_HOME=%JAVA_HOME:"=%

+set JAVA_EXE=%JAVA_HOME%/bin/java.exe

+

+if exist "%JAVA_EXE%" goto init

+

+echo.

+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%

+echo.

+echo Please set the JAVA_HOME variable in your environment to match the

+echo location of your Java installation.

+

+goto fail

+

+:init

+@rem Get command-line arguments, handling Windowz variants

+

+if not "%OS%" == "Windows_NT" goto win9xME_args

+if "%@eval[2+2]" == "4" goto 4NT_args

+

+:win9xME_args

+@rem Slurp the command line arguments.

+set CMD_LINE_ARGS=

+set _SKIP=2

+

+:win9xME_args_slurp

+if "x%~1" == "x" goto execute

+

+set CMD_LINE_ARGS=%*

+goto execute

+

+:4NT_args

+@rem Get arguments from the 4NT Shell from JP Software

+set CMD_LINE_ARGS=%$

+

+:execute

+@rem Setup the command line

+

+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar

+

+@rem Execute Gradle

+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%

+

+:end

+@rem End local scope for the variables with windows NT shell

+if "%ERRORLEVEL%"=="0" goto mainEnd

+

+:fail

+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of

+rem the _cmd.exe /c_ return code!

+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1

+exit /b 1

+

+:mainEnd

+if "%OS%"=="Windows_NT" endlocal

+

+:omega

diff --git a/prebuilts/gradle/MidiSynth/screenshots/1-main.png b/prebuilts/gradle/MidiSynth/screenshots/1-main.png
new file mode 100644
index 0000000..43a4f80
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/screenshots/1-main.png
Binary files differ
diff --git a/prebuilts/gradle/MidiSynth/screenshots/icon-web.png b/prebuilts/gradle/MidiSynth/screenshots/icon-web.png
new file mode 100644
index 0000000..dfd5a51
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/screenshots/icon-web.png
Binary files differ
diff --git a/prebuilts/gradle/MidiSynth/settings.gradle b/prebuilts/gradle/MidiSynth/settings.gradle
new file mode 100644
index 0000000..0a5c310
--- /dev/null
+++ b/prebuilts/gradle/MidiSynth/settings.gradle
@@ -0,0 +1,2 @@
+
+include 'Application'
diff --git a/prebuilts/gradle/NavigationDrawer/Application/src/main/java/com/example/android/navigationdrawer/NavigationDrawerActivity.java b/prebuilts/gradle/NavigationDrawer/Application/src/main/java/com/example/android/navigationdrawer/NavigationDrawerActivity.java
index 1176757..26d677b 100644
--- a/prebuilts/gradle/NavigationDrawer/Application/src/main/java/com/example/android/navigationdrawer/NavigationDrawerActivity.java
+++ b/prebuilts/gradle/NavigationDrawer/Application/src/main/java/com/example/android/navigationdrawer/NavigationDrawerActivity.java
@@ -27,7 +27,6 @@
 import android.support.v4.app.ActionBarDrawerToggle;
 import android.support.v4.view.GravityCompat;
 import android.support.v4.widget.DrawerLayout;
-import android.support.v7.widget.LinearLayoutManager;
 import android.support.v7.widget.RecyclerView;
 import android.view.LayoutInflater;
 import android.view.Menu;
@@ -89,7 +88,6 @@
         mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START);
         // improve performance by indicating the list if fixed size.
         mDrawerList.setHasFixedSize(true);
-        mDrawerList.setLayoutManager(new LinearLayoutManager(this));
 
         // set up the drawer's list view with items and click listener
         mDrawerList.setAdapter(new PlanetAdapter(mPlanetTitles, this));
diff --git a/prebuilts/gradle/NavigationDrawer/Application/src/main/res/layout/activity_navigation_drawer.xml b/prebuilts/gradle/NavigationDrawer/Application/src/main/res/layout/activity_navigation_drawer.xml
index 4e61639..673ef63 100644
--- a/prebuilts/gradle/NavigationDrawer/Application/src/main/res/layout/activity_navigation_drawer.xml
+++ b/prebuilts/gradle/NavigationDrawer/Application/src/main/res/layout/activity_navigation_drawer.xml
@@ -19,9 +19,12 @@
 <!-- A DrawerLayout is intended to be used as the top-level content view using match_parent for both width and height to consume the full space available. -->
 <android.support.v4.widget.DrawerLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
     android:id="@+id/drawer_layout"
     android:layout_width="match_parent"
-    android:layout_height="match_parent">
+    android:layout_height="match_parent"
+    tools:openDrawer="start" >
 
     <!-- As the main content view, the view below consumes the entire
          space available using match_parent in both dimensions. -->
@@ -44,5 +47,6 @@
         android:layout_gravity="left|start"
         android:choiceMode="singleChoice"
         android:divider="@null"
+        app:layoutManager="LinearLayoutManager"
         />
-</android.support.v4.widget.DrawerLayout>
\ No newline at end of file
+</android.support.v4.widget.DrawerLayout>
diff --git a/prebuilts/gradle/RuntimePermissions/Application/src/main/AndroidManifest.xml b/prebuilts/gradle/RuntimePermissions/Application/src/main/AndroidManifest.xml
index 0acbb93..050b051 100644
--- a/prebuilts/gradle/RuntimePermissions/Application/src/main/AndroidManifest.xml
+++ b/prebuilts/gradle/RuntimePermissions/Application/src/main/AndroidManifest.xml
@@ -34,7 +34,7 @@
         android:allowBackup="true"
         android:icon="@mipmap/ic_launcher"
         android:label="@string/app_name"
-        android:theme="@style/Theme.AppCompat.Light" >
+        android:theme="@style/AppTheme" >
         <activity
             android:name=".MainActivity"
             android:label="@string/app_name" >
diff --git a/prebuilts/gradle/RuntimePermissionsBasic/Application/src/main/AndroidManifest.xml b/prebuilts/gradle/RuntimePermissionsBasic/Application/src/main/AndroidManifest.xml
index bfae9b9..1f7ea6a 100644
--- a/prebuilts/gradle/RuntimePermissionsBasic/Application/src/main/AndroidManifest.xml
+++ b/prebuilts/gradle/RuntimePermissionsBasic/Application/src/main/AndroidManifest.xml
@@ -28,7 +28,7 @@
             android:allowBackup="true"
             android:icon="@mipmap/ic_launcher"
             android:label="@string/app_name"
-            android:theme="@style/Theme.AppCompat.Light">
+            android:theme="@style/AppTheme">
         <activity
                 android:name=".MainActivity"
                 android:label="@string/app_name">
diff --git a/prebuilts/gradle/XYZTouristAttractions/Application/src/main/java/com/example/android/xyztouristattractions/ui/AttractionListFragment.java b/prebuilts/gradle/XYZTouristAttractions/Application/src/main/java/com/example/android/xyztouristattractions/ui/AttractionListFragment.java
index 28f9127..e86ac3f 100644
--- a/prebuilts/gradle/XYZTouristAttractions/Application/src/main/java/com/example/android/xyztouristattractions/ui/AttractionListFragment.java
+++ b/prebuilts/gradle/XYZTouristAttractions/Application/src/main/java/com/example/android/xyztouristattractions/ui/AttractionListFragment.java
@@ -23,7 +23,6 @@
 import android.os.Bundle;
 import android.support.v4.app.Fragment;
 import android.support.v4.content.LocalBroadcastManager;
-import android.support.v7.widget.GridLayoutManager;
 import android.support.v7.widget.RecyclerView;
 import android.text.TextUtils;
 import android.view.LayoutInflater;
@@ -79,8 +78,6 @@
                 (AttractionsRecyclerView) view.findViewById(android.R.id.list);
         recyclerView.setEmptyView(view.findViewById(android.R.id.empty));
         recyclerView.setHasFixedSize(true);
-        recyclerView.setLayoutManager(new GridLayoutManager(
-                getActivity(), getResources().getInteger(R.integer.list_columns)));
         recyclerView.setAdapter(mAdapter);
 
         return view;
diff --git a/prebuilts/gradle/XYZTouristAttractions/Application/src/main/res/layout/fragment_main.xml b/prebuilts/gradle/XYZTouristAttractions/Application/src/main/res/layout/fragment_main.xml
index 94d110a..c92a2a8 100644
--- a/prebuilts/gradle/XYZTouristAttractions/Application/src/main/res/layout/fragment_main.xml
+++ b/prebuilts/gradle/XYZTouristAttractions/Application/src/main/res/layout/fragment_main.xml
@@ -16,6 +16,7 @@
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:orientation="vertical"
@@ -25,7 +26,9 @@
         android:id="@android:id/list"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:scrollbars="vertical" />
+        android:scrollbars="vertical"
+        app:layoutManager="GridLayoutManager"
+        app:spanCount="@integer/list_columns" />
 
     <TextView
         android:id="@android:id/empty"