isundil 4 жил өмнө
parent
commit
11d64258ec

+ 1 - 1
app/src/main/AndroidManifest.xml

@@ -19,7 +19,7 @@
             </intent-filter>
         </activity>
 
-        <receiver android:name=".BootReceiver" android:enabled="true" android:exported="false">
+        <receiver android:name=".receiver.BootReceiver" android:enabled="true" android:exported="false">
             <intent-filter>
                 <category android:name="android.intent.category.DEFAULT"/>
                 <action android:name="android.intent.action.BOOT_COMPLETED"/>

BIN
app/src/main/ic_launcher_idle-playstore.png


+ 39 - 26
app/src/main/java/info/knacki/prometheusandroidexporter/HttpService.java

@@ -1,7 +1,5 @@
 package info.knacki.prometheusandroidexporter;
 
-import android.content.Context;
-
 import com.sun.net.httpserver.HttpExchange;
 import com.sun.net.httpserver.HttpHandler;
 import com.sun.net.httpserver.HttpServer;
@@ -21,33 +19,48 @@ public class HttpService {
     private static final HttpService gService = new HttpService();
     private HttpServer fServer = null;
 
-    public static void Register(Context ctx) throws IOException {
+    public static void Register() throws IOException {
         if (gService.fServer == null) {
-            gService.fServer = HttpServer.create(new InetSocketAddress(PORT), 0);
-            gService.fServer.setExecutor(Executors.newCachedThreadPool());
-            gService.fServer.createContext("/", new HttpHandler() {
-                @Override
-                public void handle(HttpExchange httpExchange) {
-                    try {
-                        gService.ServeRoot(httpExchange);
-                    }
-                    catch (IOException e) {
-                        gLogger.log(Level.SEVERE, "Cannot serve url /", e);
-                    }
+            synchronized (HttpService.class) {
+                if (gService.fServer == null) {
+                    gService.fServer = HttpServer.create(new InetSocketAddress(PORT), 0);
+                    gService.fServer.setExecutor(Executors.newCachedThreadPool());
+                    gService.fServer.createContext("/", new HttpHandler() {
+                        @Override
+                        public void handle(HttpExchange httpExchange) {
+                            try {
+                                gService.ServeRoot(httpExchange);
+                            }
+                            catch (IOException e) {
+                                gLogger.log(Level.SEVERE, "Cannot serve url /", e);
+                            }
+                        }
+                    });
+                    gService.fServer.createContext("/metrics", new HttpHandler() {
+                        @Override
+                        public void handle(HttpExchange httpExchange) {
+                            try {
+                                gService.ServeMetrics(httpExchange);
+                            }
+                            catch (IOException e) {
+                                gLogger.log(Level.SEVERE, "Cannot serve url /", e);
+                            }
+                        }
+                    });
+                    gService.fServer.start();
                 }
-            });
-            gService.fServer.createContext("/metrics", new HttpHandler() {
-                @Override
-                public void handle(HttpExchange httpExchange) {
-                    try {
-                        gService.ServeMetrics(httpExchange);
-                    }
-                    catch (IOException e) {
-                        gLogger.log(Level.SEVERE, "Cannot serve url /", e);
-                    }
+            }
+        }
+    }
+
+    public static void Stop() {
+        if (gService.fServer != null) {
+            synchronized (HttpService.class) {
+                if (gService.fServer != null) {
+                    gService.fServer.stop(1);
+                    gService.fServer = null;
                 }
-            });
-            gService.fServer.start();
+            }
         }
     }
 

+ 70 - 15
app/src/main/java/info/knacki/prometheusandroidexporter/MainService.java

@@ -1,5 +1,6 @@
 package info.knacki.prometheusandroidexporter;
 
+import android.app.ActivityManager;
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
@@ -18,13 +19,21 @@ import java.util.concurrent.TimeUnit;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
+import info.knacki.prometheusandroidexporter.collector.CPUCollector;
 import info.knacki.prometheusandroidexporter.collector.DeviceCollector;
 import info.knacki.prometheusandroidexporter.collector.MemoryCollector;
 import info.knacki.prometheusandroidexporter.collector.NetworkCollector;
 import info.knacki.prometheusandroidexporter.collector.PowerCollector;
+import info.knacki.prometheusandroidexporter.collector.ProcessCollector;
 import info.knacki.prometheusandroidexporter.collector.StorageCollector;
+import info.knacki.prometheusandroidexporter.receiver.NetworkReceiver;
 
 public class MainService extends Service {
+    public final static Logger log = Logger.getLogger(MainService.class.getName());
+    private final Binder fLocalBinder = this.new Binder();
+    private Notification.Builder fNotif;
+    private boolean fPaused;
+
     public MainService() {
     }
 
@@ -48,30 +57,69 @@ public class MainService extends Service {
         CollectorManager collectorManager = CollectorManager.GetInstance();
         collectorManager.tick(this);
         InitCollectors(collectorManager);
-        ScheduleCollection();
-        try {
-            HttpService.Register(getApplicationContext());
-        }
-        catch (IOException e) {
-            Logger.getLogger(MainService.class.getName()).log(Level.SEVERE, "Cannot start server: ", e);
-        }
         NotifyIcon();
+        fPaused = true;
+        NetworkReceiver.Register(this);
+    }
+
+    public class Binder extends android.os.Binder {
+        public MainService GetService() {
+            return MainService.this;
+        }
     }
 
     @Nullable
     @Override
     public IBinder onBind(Intent intent) {
-        return null;
+        return fLocalBinder;
     }
 
     private void InitCollectors(CollectorManager manager) {
         //manager.RegisterCollector(new TestCollector());
-        manager.RegisterCollector(new DeviceCollector());
+        manager.RegisterCollector(new DeviceCollector(this));
         manager.RegisterCollector(new PowerCollector());
         manager.RegisterCollector(new MemoryCollector());
         manager.RegisterCollector(new NetworkCollector());
+        manager.RegisterCollector(new ProcessCollector());
+
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2)
             manager.RegisterCollector(new StorageCollector());
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
+            manager.RegisterCollector(new CPUCollector());
+    }
+
+    public void Resume() {
+        if (fPaused) {
+            synchronized (this) {
+                if (fPaused) {
+                    log.warning("RESUME!~~~~");
+                    try {
+                        HttpService.Register();
+                    }
+                    catch (IOException e) {
+                        log.log(Level.SEVERE, "Cannot start http server: ", e);
+                        return;
+                    }
+                    ScheduleCollection();
+                    UpdateIcon(R.mipmap.ic_launcher, "Service is running");
+                    fPaused = false;
+                }
+            }
+        }
+    }
+
+    public void Pause() {
+        if (!fPaused) {
+            synchronized (this) {
+                if (!fPaused) {
+                    log.warning("PAUSE!~~~~");
+                    fPaused = true;
+                    HttpService.Stop();
+                    WorkManager.getInstance(this).cancelAllWork();
+                    UpdateIcon(R.mipmap.ic_launcher_idle, "Service waiting for wifi");
+                }
+            }
+        }
     }
 
     private void ScheduleCollection() {
@@ -79,14 +127,21 @@ public class MainService extends Service {
         WorkManager.getInstance(this).enqueue(job);
     }
 
-    private void NotifyIcon() {
-        Notification notif = (new Notification.Builder(this)
-            .setContentIntent(PendingIntent.getActivity(this, 0, new Intent(this, MainActivity.class), PendingIntent.FLAG_UPDATE_CURRENT))
-            .setSmallIcon(R.mipmap.ic_launcher)
-            .setContentTitle("Prometheus Android Exporter")
-            .setContentText("Service running")).build();
+    private void UpdateIcon(int icon, String text) {
+        Notification notif = fNotif
+                .setSmallIcon(icon)
+                .setContentText(text)
+                .build();
         notif.flags = Notification.FLAG_ONGOING_EVENT;
         ((NotificationManager) getSystemService(NOTIFICATION_SERVICE)).notify(1, notif);
+    }
+
+    private void NotifyIcon() {
+        fNotif = new Notification.Builder(this)
+            .setContentIntent(PendingIntent.getActivity(this, 0, new Intent(this, MainActivity.class), PendingIntent.FLAG_UPDATE_CURRENT))
+            .setContentTitle("Prometheus Android Exporter");
+        Notification notif = fNotif.build();
+        UpdateIcon(R.mipmap.ic_launcher_idle, "Service booting");
         startForeground(1, notif);
     }
 }

+ 24 - 0
app/src/main/java/info/knacki/prometheusandroidexporter/collector/CPUCollector.java

@@ -0,0 +1,24 @@
+package info.knacki.prometheusandroidexporter.collector;
+
+import android.content.Context;
+import android.os.Build;
+import android.os.HardwarePropertiesManager;
+
+import androidx.annotation.RequiresApi;
+
+import java.util.ArrayDeque;
+import java.util.Collection;
+
+import info.knacki.prometheusandroidexporter.CollectorType;
+import info.knacki.prometheusandroidexporter.ICollector;
+
+@RequiresApi(api = Build.VERSION_CODES.N)
+public class CPUCollector implements ICollector {
+    @Override
+    public Collection<CollectorType.CollectorValue> ReadValues(Context ctx) {
+        ArrayDeque<CollectorType.CollectorValue> result = new ArrayDeque<>();
+        HardwarePropertiesManager hardwareManager = ctx.getSystemService(HardwarePropertiesManager.class);
+        // FIXME monitor CPU time, cpu temp
+        return result;
+    }
+}

+ 55 - 8
app/src/main/java/info/knacki/prometheusandroidexporter/collector/DeviceCollector.java

@@ -1,34 +1,81 @@
 package info.knacki.prometheusandroidexporter.collector;
 
 import android.content.Context;
+import android.os.Build;
 import android.os.SystemClock;
+import android.provider.Settings;
 
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.InputStreamReader;
 import java.util.ArrayDeque;
 import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
 import info.knacki.prometheusandroidexporter.CollectorType;
 import info.knacki.prometheusandroidexporter.CollectorType.CollectorValue;
 import info.knacki.prometheusandroidexporter.ICollector;
 
 public class DeviceCollector implements ICollector {
+    public final static Logger log = Logger.getLogger(DeviceCollector.class.getName());
     private final CollectorType deviceUptimeCollector = new CollectorType("device_uptime_sec", "Device uptime", CollectorType.Type.COUNTER);
     private final double fAndroidBootTime;
+    private final CollectorType.CollectorValue fUname;
+    private final CollectorType.CollectorValue fCpuInfos;
 
-    public DeviceCollector() {
-        fAndroidBootTime = (System.currentTimeMillis() - SystemClock.uptimeMillis());
+    private boolean ReadCpuInfo(Map<String, String> values) {
+        try {
+            FileInputStream io = new FileInputStream("/proc/cpuinfo");
+            BufferedReader reader = new BufferedReader(new InputStreamReader(io));
+            String line;
+            while ((line = reader.readLine()) != null) {
+                String[] infos = line.split("\\s*:\\s*", 2);
+                if (infos.length == 2)
+                    values.put(infos[0].replaceAll("\\s+", "_").toLowerCase(), infos[1]);
+            }
+            reader.close();
+            return true;
+        }
+        catch (Exception e) {
+            log.log(Level.SEVERE, "Cannot read from cpuinfo file", e);
+            return false;
+        }
     }
 
-    private CollectorValue Uptime() {
-        CollectorValue val = deviceUptimeCollector.new CollectorValue();
-        double uptime = System.currentTimeMillis() - fAndroidBootTime;
-        val.SetValue((int) Math.round(uptime / 1000.));
-        return val;
+    public DeviceCollector(Context ctx) {
+        fAndroidBootTime = (System.currentTimeMillis() - SystemClock.uptimeMillis());
+        fUname = (new CollectorType("device_info", "Device info", CollectorType.Type.GAUGE)).new CollectorValue()
+                .SetValue(1)
+                .AddParameter("brand", Build.BRAND)
+                .AddParameter("device", Build.DEVICE)
+                .AddParameter("hardware", Build.HARDWARE)
+                .AddParameter("host", Build.HOST)
+                .AddParameter("id", Build.ID)
+                .AddParameter("manufacturer", Build.MANUFACTURER)
+                .AddParameter("model", Build.MODEL)
+                .AddParameter("product", Build.PRODUCT)
+                .AddParameter("radio_version", Build.getRadioVersion())
+                .AddParameter("bluetooth_hostname", Settings.Secure.getString(ctx.getContentResolver(), "bluetooth_name"));
+        fUname.AddParameter("partition_name_system", Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q ? Build.Partition.PARTITION_NAME_SYSTEM : "Unknown");
+        fCpuInfos = (new CollectorType("device_cpu", "CPU Infos", CollectorType.Type.GAUGE)).new CollectorValue().SetValue(0);
+
+        Map<String, String> cpuInfos = new HashMap<>();
+        if (ReadCpuInfo(cpuInfos)) {
+            fCpuInfos.SetValue(1);
+            for (Map.Entry<String, String> i: cpuInfos.entrySet())
+                fCpuInfos.AddParameter(i.getKey(), i.getValue());
+        }
     }
 
     @Override
     public Collection<CollectorValue> ReadValues(Context ctx) {
         ArrayDeque<CollectorValue> result = new ArrayDeque<>();
-        result.add(Uptime());
+        result.add(deviceUptimeCollector.new CollectorValue().SetValue((int) Math.round((System.currentTimeMillis() - fAndroidBootTime) / 1000.)));
+        result.add(fUname);
+        result.add(fCpuInfos);
         return result;
     }
 }

+ 4 - 4
app/src/main/java/info/knacki/prometheusandroidexporter/collector/NetworkCollector.java

@@ -19,16 +19,16 @@ public class NetworkCollector implements ICollector {
 
         result.add(netRxBytes.new CollectorValue()
                 .SetValue(TrafficStats.getMobileRxBytes())
-                .AddParameter("sources", "mobile"));
+                .AddParameter("source", "mobile"));
         result.add(netRxBytes.new CollectorValue()
                 .SetValue(TrafficStats.getTotalRxBytes())
-                .AddParameter("sources", "total"));
+                .AddParameter("source", "total"));
         result.add(netTxBytes.new CollectorValue()
                 .SetValue(TrafficStats.getMobileTxBytes())
-                .AddParameter("sources", "mobile"));
+                .AddParameter("source", "mobile"));
         result.add(netTxBytes.new CollectorValue()
                 .SetValue(TrafficStats.getTotalTxBytes())
-                .AddParameter("sources", "total"));
+                .AddParameter("source", "total"));
         return result;
     }
 }

+ 37 - 0
app/src/main/java/info/knacki/prometheusandroidexporter/collector/ProcessCollector.java

@@ -0,0 +1,37 @@
+package info.knacki.prometheusandroidexporter.collector;
+
+import android.app.ActivityManager;
+import android.content.Context;
+
+import java.util.ArrayDeque;
+import java.util.Collection;
+import java.util.List;
+
+import info.knacki.prometheusandroidexporter.CollectorType;
+import info.knacki.prometheusandroidexporter.ICollector;
+
+public class ProcessCollector implements ICollector {
+    private static class Data {
+        public final int fProcessCount;
+        public final int fRunningAppProcess;
+        public final int fTaskCount;
+        //public final int fProcessCount;
+
+        Data(Context ctx) {
+            final ActivityManager activityManager = (ActivityManager) ctx.getSystemService(Context.ACTIVITY_SERVICE);
+            fProcessCount = activityManager.getRunningServices(Integer.MAX_VALUE).size();
+            fRunningAppProcess = activityManager.getRunningAppProcesses().size();
+            fTaskCount = activityManager.getRunningTasks(Integer.MAX_VALUE).size();
+        }
+    }
+
+    @Override
+    public Collection<CollectorType.CollectorValue> ReadValues(Context ctx) {
+        ArrayDeque<CollectorType.CollectorValue> result = new ArrayDeque<>();
+        Data d = new Data(ctx);
+        result.add(new CollectorType("process_service_count", "Android running services", CollectorType.Type.GAUGE).new CollectorValue().SetValue(d.fProcessCount));
+        result.add(new CollectorType("process_running_app_process_count", "Android running application process count", CollectorType.Type.GAUGE).new CollectorValue().SetValue(d.fRunningAppProcess));
+        result.add(new CollectorType("process_running_tasks_count", "Android running tasks", CollectorType.Type.GAUGE).new CollectorValue().SetValue(d.fTaskCount));
+        return result;
+    }
+}

+ 3 - 1
app/src/main/java/info/knacki/prometheusandroidexporter/BootReceiver.java → app/src/main/java/info/knacki/prometheusandroidexporter/receiver/BootReceiver.java

@@ -1,9 +1,11 @@
-package info.knacki.prometheusandroidexporter;
+package info.knacki.prometheusandroidexporter.receiver;
 
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 
+import info.knacki.prometheusandroidexporter.MainService;
+
 public class BootReceiver extends BroadcastReceiver {
     @Override
     public void onReceive(Context context, Intent intent) {

+ 93 - 0
app/src/main/java/info/knacki/prometheusandroidexporter/receiver/NetworkReceiver.java

@@ -0,0 +1,93 @@
+package info.knacki.prometheusandroidexporter.receiver;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.wifi.WifiManager;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+
+import info.knacki.prometheusandroidexporter.MainService;
+
+public class NetworkReceiver extends BroadcastReceiver {
+    private static NetworkReceiver gInstance = null;
+
+    private NetworkReceiver() {}
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        Tick(context);
+    }
+
+    public static void Register(MainService ctx) {
+        synchronized (NetworkReceiver.class) {
+            if (gInstance == null) {
+                synchronized (NetworkReceiver.class) {
+                    IntentFilter intentFilter = new IntentFilter();
+                    intentFilter.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
+                    intentFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
+                    gInstance = new NetworkReceiver();
+                    ctx.registerReceiver(gInstance, intentFilter);
+                    gInstance.Tick(ctx);
+                }
+            }
+        }
+    }
+
+    public boolean IsConnected(Context ctx) {
+        ConnectivityManager manager = (ConnectivityManager) ctx.getSystemService(Context.CONNECTIVITY_SERVICE);
+        NetworkInfo netInfo = manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
+        if (netInfo != null && netInfo.isConnected())
+            return true;
+        netInfo = manager.getNetworkInfo(ConnectivityManager.TYPE_ETHERNET);
+        return netInfo != null && netInfo.isConnected();
+    }
+
+    private interface IServiceRunnable {
+        void Run(MainService service);
+    }
+
+    private void GetService(final Context ctx, final IServiceRunnable onBound) {
+        if (ctx instanceof MainService) {
+            onBound.Run((MainService) ctx);
+            return;
+        }
+        ctx.bindService(new Intent(ctx, MainService.class), new ServiceConnection() {
+            @Override
+            public void onServiceConnected(ComponentName name, IBinder service) {
+                if (service instanceof MainService.Binder)
+                    onBound.Run(((MainService.Binder) service).GetService());
+                ctx.unbindService(this);
+            }
+
+            @Override
+            public void onServiceDisconnected(ComponentName name) {
+            }
+        }, Context.BIND_AUTO_CREATE);
+    }
+
+    public void Tick(Context ctx) {
+        GetService(ctx, new IServiceRunnable() {
+            @Override
+            public void Run(final MainService service) {
+                Runnable r = new Runnable() {
+                    @Override
+                    public void run() {
+                        if (IsConnected(service))
+                            service.Resume();
+                        else
+                            service.Pause();
+                    }
+                };
+                (new Handler(Looper.getMainLooper())).postDelayed(r, 5000);
+                r.run();
+            }
+        });
+    }
+}

BIN
app/src/main/res/ic_launcher_idle.png


BIN
app/src/main/res/mipmap-hdpi/ic_launcher_idle.png


BIN
app/src/main/res/mipmap-mdpi/ic_launcher_idle.png


BIN
app/src/main/res/mipmap-xhdpi/ic_launcher_idle.png


BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher_idle.png


BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher_idle.png