• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.launcher3;
18 
19 import android.content.BroadcastReceiver;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.IntentFilter;
23 import android.util.Log;
24 
25 import java.io.*;
26 import java.util.ArrayList;
27 
28 public class Stats {
29     private static final boolean DEBUG_BROADCASTS = false;
30     private static final String TAG = "Launcher3/Stats";
31 
32     private static final boolean LOCAL_LAUNCH_LOG = true;
33 
34     public static final String ACTION_LAUNCH = "com.android.launcher3.action.LAUNCH";
35     public static final String PERM_LAUNCH = "com.android.launcher3.permission.RECEIVE_LAUNCH_BROADCASTS";
36     public static final String EXTRA_INTENT = "intent";
37     public static final String EXTRA_CONTAINER = "container";
38     public static final String EXTRA_SCREEN = "screen";
39     public static final String EXTRA_CELLX = "cellX";
40     public static final String EXTRA_CELLY = "cellY";
41 
42     private static final String LOG_FILE_NAME = "launches.log";
43     private static final int LOG_VERSION = 1;
44     private static final int LOG_TAG_VERSION = 0x1;
45     private static final int LOG_TAG_LAUNCH = 0x1000;
46 
47     private static final String STATS_FILE_NAME = "stats.log";
48     private static final int STATS_VERSION = 1;
49     private static final int INITIAL_STATS_SIZE = 100;
50 
51     // TODO: delayed/batched writes
52     private static final boolean FLUSH_IMMEDIATELY = true;
53 
54     private final Launcher mLauncher;
55 
56     DataOutputStream mLog;
57 
58     ArrayList<String> mIntents;
59     ArrayList<Integer> mHistogram;
60 
Stats(Launcher launcher)61     public Stats(Launcher launcher) {
62         mLauncher = launcher;
63 
64         loadStats();
65 
66         if (LOCAL_LAUNCH_LOG) {
67             try {
68                 mLog = new DataOutputStream(mLauncher.openFileOutput(LOG_FILE_NAME, Context.MODE_APPEND));
69                 mLog.writeInt(LOG_TAG_VERSION);
70                 mLog.writeInt(LOG_VERSION);
71             } catch (FileNotFoundException e) {
72                 Log.e(TAG, "unable to create stats log: " + e);
73                 mLog = null;
74             } catch (IOException e) {
75                 Log.e(TAG, "unable to write to stats log: " + e);
76                 mLog = null;
77             }
78         }
79 
80         if (DEBUG_BROADCASTS) {
81             launcher.registerReceiver(
82                     new BroadcastReceiver() {
83                         @Override
84                         public void onReceive(Context context, Intent intent) {
85                             android.util.Log.v("Stats", "got broadcast: " + intent + " for launched intent: "
86                                     + intent.getStringExtra(EXTRA_INTENT));
87                         }
88                     },
89                     new IntentFilter(ACTION_LAUNCH),
90                     PERM_LAUNCH,
91                     null
92             );
93         }
94     }
95 
incrementLaunch(String intentStr)96     public void incrementLaunch(String intentStr) {
97         int pos = mIntents.indexOf(intentStr);
98         if (pos < 0) {
99             mIntents.add(intentStr);
100             mHistogram.add(1);
101         } else {
102             mHistogram.set(pos, mHistogram.get(pos) + 1);
103         }
104     }
105 
recordLaunch(Intent intent)106     public void recordLaunch(Intent intent) {
107         recordLaunch(intent, null);
108     }
109 
recordLaunch(Intent intent, ShortcutInfo shortcut)110     public void recordLaunch(Intent intent, ShortcutInfo shortcut) {
111         intent = new Intent(intent);
112         intent.setSourceBounds(null);
113 
114         final String flat = intent.toUri(0);
115 
116         Intent broadcastIntent = new Intent(ACTION_LAUNCH).putExtra(EXTRA_INTENT, flat);
117         if (shortcut != null) {
118             broadcastIntent.putExtra(EXTRA_CONTAINER, shortcut.container)
119                     .putExtra(EXTRA_SCREEN, shortcut.screenId)
120                     .putExtra(EXTRA_CELLX, shortcut.cellX)
121                     .putExtra(EXTRA_CELLY, shortcut.cellY);
122         }
123         mLauncher.sendBroadcast(broadcastIntent, PERM_LAUNCH);
124 
125         incrementLaunch(flat);
126 
127         if (FLUSH_IMMEDIATELY) {
128             saveStats();
129         }
130 
131         if (LOCAL_LAUNCH_LOG && mLog != null) {
132             try {
133                 mLog.writeInt(LOG_TAG_LAUNCH);
134                 mLog.writeLong(System.currentTimeMillis());
135                 if (shortcut == null) {
136                     mLog.writeShort(0);
137                     mLog.writeShort(0);
138                     mLog.writeShort(0);
139                     mLog.writeShort(0);
140                 } else {
141                     mLog.writeShort((short) shortcut.container);
142                     mLog.writeShort((short) shortcut.screenId);
143                     mLog.writeShort((short) shortcut.cellX);
144                     mLog.writeShort((short) shortcut.cellY);
145                 }
146                 mLog.writeUTF(flat);
147                 if (FLUSH_IMMEDIATELY) {
148                     mLog.flush(); // TODO: delayed writes
149                 }
150             } catch (IOException e) {
151                 e.printStackTrace();
152             }
153         }
154     }
155 
saveStats()156     private void saveStats() {
157         DataOutputStream stats = null;
158         try {
159             stats = new DataOutputStream(mLauncher.openFileOutput(STATS_FILE_NAME + ".tmp", Context.MODE_PRIVATE));
160             stats.writeInt(STATS_VERSION);
161             final int N = mHistogram.size();
162             stats.writeInt(N);
163             for (int i=0; i<N; i++) {
164                 stats.writeUTF(mIntents.get(i));
165                 stats.writeInt(mHistogram.get(i));
166             }
167             stats.close();
168             stats = null;
169             mLauncher.getFileStreamPath(STATS_FILE_NAME + ".tmp")
170                      .renameTo(mLauncher.getFileStreamPath(STATS_FILE_NAME));
171         } catch (FileNotFoundException e) {
172             Log.e(TAG, "unable to create stats data: " + e);
173         } catch (IOException e) {
174             Log.e(TAG, "unable to write to stats data: " + e);
175         } finally {
176             if (stats != null) {
177                 try {
178                     stats.close();
179                 } catch (IOException e) { }
180             }
181         }
182     }
183 
loadStats()184     private void loadStats() {
185         mIntents = new ArrayList<String>(INITIAL_STATS_SIZE);
186         mHistogram = new ArrayList<Integer>(INITIAL_STATS_SIZE);
187         DataInputStream stats = null;
188         try {
189             stats = new DataInputStream(mLauncher.openFileInput(STATS_FILE_NAME));
190             final int version = stats.readInt();
191             if (version == STATS_VERSION) {
192                 final int N = stats.readInt();
193                 for (int i=0; i<N; i++) {
194                     final String pkg = stats.readUTF();
195                     final int count = stats.readInt();
196                     mIntents.add(pkg);
197                     mHistogram.add(count);
198                 }
199             }
200         } catch (FileNotFoundException e) {
201             // not a problem
202         } catch (IOException e) {
203             // more of a problem
204 
205         } finally {
206             if (stats != null) {
207                 try {
208                     stats.close();
209                 } catch (IOException e) { }
210             }
211         }
212     }
213 }
214