summaryrefslogtreecommitdiffstats
path: root/src/com/android/launcher3/Stats.java
diff options
context:
space:
mode:
authorDaniel Sandler <dsandler@android.com>2013-08-05 02:12:05 -0400
committerDaniel Sandler <dsandler@android.com>2013-08-06 00:18:38 -0400
commitff02d49e464c2fe92ba625a3046f31aa042e5d32 (patch)
tree8b766db2f5690e011fa0c2c2b3c235c593c9fdc4 /src/com/android/launcher3/Stats.java
parent482a5b6ed389ef943990277e461444626c34ebf2 (diff)
downloadandroid_packages_apps_Trebuchet-ff02d49e464c2fe92ba625a3046f31aa042e5d32.tar.gz
android_packages_apps_Trebuchet-ff02d49e464c2fe92ba625a3046f31aa042e5d32.tar.bz2
android_packages_apps_Trebuchet-ff02d49e464c2fe92ba625a3046f31aa042e5d32.zip
Initial implementation: Broadcasts on app launch.
Look for com.android.launcher3.action.LAUNCH to be sent when an icon is clicked in Launcher. (Restricted to com.android.launcher3.permission.RECEIVE_LAUNCH_BROADCASTS which is a signature permission right now. This is specifically tracking apps launched via shortcut icon; any other method of launching apps (notifications, recents, internal navigation, etc.) is outside of Launcher's purview and hence not broadcast. The broadcast currently includes, in the "intent" extra, the Uri flattening of the specific shortcut clicked. The file /data/data/<pkg>/files/launches.log contains a binary log of all such launches, including additional info like screen# that should probably be in the broadcast too. This info is summarized in .../stats.log, which encodes a simple histogram of app launches since basically forever. This should probably be done over a sliding window, which will require more processing on startup. Bug: 10031590 Change-Id: Ifc5921d5dc20701c67678cbfdc89b03cacd62028
Diffstat (limited to 'src/com/android/launcher3/Stats.java')
-rw-r--r--src/com/android/launcher3/Stats.java202
1 files changed, 202 insertions, 0 deletions
diff --git a/src/com/android/launcher3/Stats.java b/src/com/android/launcher3/Stats.java
new file mode 100644
index 000000000..ca088f71a
--- /dev/null
+++ b/src/com/android/launcher3/Stats.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2012 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.android.launcher3;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.util.Log;
+
+import java.io.*;
+import java.util.ArrayList;
+
+public class Stats {
+ private static final boolean DEBUG_BROADCASTS = false;
+ private static final String TAG = "Launcher3/Stats";
+
+ private static final boolean LOCAL_LAUNCH_LOG = true;
+
+ public static final String ACTION_LAUNCH = "com.android.launcher3.action.LAUNCH";
+ public static final String PERM_LAUNCH = "com.android.launcher3.permission.RECEIVE_LAUNCH_BROADCASTS";
+ public static final String EXTRA_INTENT = "intent";
+ public static final String EXTRA_CONTAINER = "container";
+ public static final String EXTRA_SCREEN = "screen";
+ public static final String EXTRA_CELLX = "cellX";
+ public static final String EXTRA_CELLY = "cellY";
+
+ private static final String LOG_FILE_NAME = "launches.log";
+ private static final int LOG_VERSION = 1;
+ private static final int LOG_TAG_VERSION = 0x1;
+ private static final int LOG_TAG_LAUNCH = 0x1000;
+
+ private static final String STATS_FILE_NAME = "stats.log";
+ private static final int STATS_VERSION = 1;
+ private static final int INITIAL_STATS_SIZE = 100;
+
+ // TODO: delayed/batched writes
+ private static final boolean FLUSH_IMMEDIATELY = true;
+
+ private final Launcher mLauncher;
+
+ DataOutputStream mLog;
+
+ ArrayList<String> mIntents;
+ ArrayList<Integer> mHistogram;
+
+ public Stats(Launcher launcher) {
+ mLauncher = launcher;
+
+ loadStats();
+
+ if (LOCAL_LAUNCH_LOG) {
+ try {
+ mLog = new DataOutputStream(mLauncher.openFileOutput(LOG_FILE_NAME, Context.MODE_APPEND));
+ mLog.writeInt(LOG_TAG_VERSION);
+ mLog.writeInt(LOG_VERSION);
+ } catch (FileNotFoundException e) {
+ Log.e(TAG, "unable to create stats log: " + e);
+ mLog = null;
+ } catch (IOException e) {
+ Log.e(TAG, "unable to write to stats log: " + e);
+ mLog = null;
+ }
+ }
+
+ if (DEBUG_BROADCASTS) {
+ launcher.registerReceiver(
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ android.util.Log.v("Stats", "got broadcast: " + intent + " for launched intent: "
+ + intent.getStringExtra(EXTRA_INTENT));
+ }
+ },
+ new IntentFilter(ACTION_LAUNCH),
+ PERM_LAUNCH,
+ null
+ );
+ }
+ }
+
+ public void incrementLaunch(String intentStr) {
+ int pos = mIntents.indexOf(intentStr);
+ if (pos < 0) {
+ mIntents.add(intentStr);
+ mHistogram.add(1);
+ } else {
+ mHistogram.set(pos, mHistogram.get(pos) + 1);
+ }
+ }
+
+ public void recordLaunch(Intent intent, ShortcutInfo shortcut) {
+ intent = new Intent(intent);
+ intent.setSourceBounds(null);
+
+ final String flat = intent.toUri(0);
+
+ mLauncher.sendBroadcast(
+ new Intent(ACTION_LAUNCH)
+ .putExtra(EXTRA_INTENT, flat)
+ .putExtra(EXTRA_CONTAINER, shortcut.container)
+ .putExtra(EXTRA_SCREEN, shortcut.screenId)
+ .putExtra(EXTRA_CELLX, shortcut.cellX)
+ .putExtra(EXTRA_CELLY, shortcut.cellY),
+ PERM_LAUNCH);
+
+ incrementLaunch(flat);
+
+ if (FLUSH_IMMEDIATELY) {
+ saveStats();
+ }
+
+ if (LOCAL_LAUNCH_LOG && mLog != null) {
+ try {
+ mLog.writeInt(LOG_TAG_LAUNCH);
+ mLog.writeLong(System.currentTimeMillis());
+ mLog.writeShort((short) shortcut.container);
+ mLog.writeShort((short) shortcut.screenId);
+ mLog.writeShort((short) shortcut.cellX);
+ mLog.writeShort((short) shortcut.cellY);
+ mLog.writeUTF(flat);
+ if (FLUSH_IMMEDIATELY) {
+ mLog.flush(); // TODO: delayed writes
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ private void saveStats() {
+ DataOutputStream stats = null;
+ try {
+ stats = new DataOutputStream(mLauncher.openFileOutput(STATS_FILE_NAME + ".tmp", Context.MODE_PRIVATE));
+ stats.writeInt(STATS_VERSION);
+ final int N = mHistogram.size();
+ stats.writeInt(N);
+ for (int i=0; i<N; i++) {
+ stats.writeUTF(mIntents.get(i));
+ stats.writeInt(mHistogram.get(i));
+ }
+ stats.close();
+ stats = null;
+ mLauncher.getFileStreamPath(STATS_FILE_NAME + ".tmp")
+ .renameTo(mLauncher.getFileStreamPath(STATS_FILE_NAME));
+ } catch (FileNotFoundException e) {
+ Log.e(TAG, "unable to create stats data: " + e);
+ } catch (IOException e) {
+ Log.e(TAG, "unable to write to stats data: " + e);
+ } finally {
+ if (stats != null) {
+ try {
+ stats.close();
+ } catch (IOException e) { }
+ }
+ }
+ }
+
+ private void loadStats() {
+ mIntents = new ArrayList<String>(INITIAL_STATS_SIZE);
+ mHistogram = new ArrayList<Integer>(INITIAL_STATS_SIZE);
+ DataInputStream stats = null;
+ try {
+ stats = new DataInputStream(mLauncher.openFileInput(STATS_FILE_NAME));
+ final int version = stats.readInt();
+ if (version == STATS_VERSION) {
+ final int N = stats.readInt();
+ for (int i=0; i<N; i++) {
+ final String pkg = stats.readUTF();
+ final int count = stats.readInt();
+ mIntents.add(pkg);
+ mHistogram.add(count);
+ }
+ }
+ } catch (FileNotFoundException e) {
+ // not a problem
+ } catch (IOException e) {
+ // more of a problem
+
+ } finally {
+ if (stats != null) {
+ try {
+ stats.close();
+ } catch (IOException e) { }
+ }
+ }
+ }
+}