Popular Posts
SwiXml - JTextBox (Customized JTextField) LimitedDocument.java package swixml.sample; import javax.swing.text.AttributeSet; import javax.swing.text.BadLocationException; import jav... Tick Start tick Java Javascript .net Tick = 0 (GMT+0) 1970/01/01 00:00:00 1970/01/01 00:00:00 1601/01/01 00:00:00... JDateField package bruce.lib.swing; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.A...
Stats
Showing posts with label android. Show all posts
Showing posts with label android. Show all posts
Build an OpenVPN server on android device

Preparation


  1. An android device, in this case, Sony xperia Z is used
  2. Root permission required
  3. Linux Deploy for deploy image via chroot/proot
  4. OpenVPN


Step

1. Root android device for superuser permission
2. Install Linux Deploy from play store or custom apk.
3. Install linux
  • Click download icon to select which image to deploy

  • Select which distribution and suite to use
  • Set user name & password
  • Allow init system
  • Enable SSH server

  • Deploy distribution

  • After installation completed, start linux

4. Connect to android via SSH
5. Install OpenVPN
  • Install required packages
  • Create certification folder and edit vars configuration
  • Edit vars as blow
  • Build ca, server and client certification

  • Copy required certification to the path of openvpn
  • Clone configuration file from sample config
  • Edit server.conf
  • Edit server.conf as blow, modify what your need
  • Modify networking config sysctl.conf
  • Modify content to allow transfer traffic from vpn
  • Make changes work
  • Edit firewall rules
  • Add following content to allow route from vpn subnet to wireless
  • Edit firewall configuration
  • Allow forward policy
  • Add firewall rule for vpn and ssh
  • Make changes work. (In this case, there was some error but I ignore that)
  • Start openvpn
  • Validate result

6. Use vpn client to connect android device
Problem on result of DexFile.entries()

Recently I upgrade my Android Studio to version 2.0, and try to build and run some app on devices. Unfortunately something goes wrong when app executing. When app invokes DexFile.entries() for the class names, the result between android 4 and 5+ is different.

Android version


Okay, let's do some simple test, here is a brand new project I've currently created. The build config and code is below:
App only invokes DexFile.entries() and show the result on a TextView.
On the result, there is a large difference between android 4.2.2 and android 5.0.0
On version 4.2.2, the listed class count is 1555, but on version 5.0.0, there are only 31 entries return.
build.gradle
apply plugin: 'com.android.application'

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.3"

    defaultConfig {
        applicationId "com.prhythm.myapplication"
        minSdkVersion 14
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:23.3.0'
}
MainActivity.java
package com.prhythm.myapplication;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.widget.TextView;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

import dalvik.system.DexFile;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        TextView text = (TextView) findViewById(R.id.content);

        try {
            List<String> paths = new ArrayList<>();
            DexFile dex = new DexFile(getApplicationInfo().sourceDir);
            for (Enumeration<String> entries = dex.entries(); entries.hasMoreElements(); ) {
                paths.add(entries.nextElement());
            }
            text.append(String.format("Total entry count: %d%n", paths.size()));
            text.append("* " + TextUtils.join("\n* ", paths));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
Run Result on android 4.2.2 & 5.0.0
Solution:

Looks like this issue is related to the new InstantRun feature in the Android Plugin for Gradle 2.0.0. getPackageCodePath() gets a String pointing towards the base.apk file in the Android file system. If we unzip that apk we can find one or several .dex files inside its root folder. The entries obtained from the method df.entries() iterates over the .dex files found in that root folder in order to obtain all of its compiled classes. However, if we are using the new Android Plugin for Gradle, we will only find the .dex related to the android runtime and instant run (packages com.tools.android.fd.runtime, com.tools.android.fd.common and com.tools.android.tools.ir.api). Every other class will be compiled in several .dex files, zipped into a file called instant-run.zip and placed into the root folder of the apk. That's why the code posted in the question is not able to list all the classes within the app. Still, this will only affect Debug builds since the Release ones don't feature InstantRun.

List<String> getClasses() throws IOException {
    List<String> paths = new ArrayList<>();
    File instantRunDir = new File(getBaseContext().getFilesDir(), "instant-run/dex");
    if (instantRunDir.exists()) {
        for (File dexPath : instantRunDir.listFiles()) {
            DexFile dex = new DexFile(dexPath);
            for (Enumeration<String> entries = dex.entries(); entries.hasMoreElements(); ) {
                paths.add(entries.nextElement());
            }
        }
    }

    DexFile dex = new DexFile(getApplicationInfo().sourceDir);
    for (Enumeration<String> entries = dex.entries(); entries.hasMoreElements(); ) {
        paths.add(entries.nextElement());
    }

    return paths;
}
Operate form in WebView and get content
class ContentLoader {
    @JavascriptInterface
    public void load(String body) {
        // do something
    }
}

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    WebView webView = createWebView();

    webView.loadUrl("https://www.somedoamin.com/product.jsp");
    // enable javascript
    webView.getSettings().setJavaScriptEnabled(true);
    // add interface
    webView.addJavascriptInterface(new ContentLoader(), "ContentLoader");
    webView.setWebViewClient(new WebViewClient() {

        int action;

        @Override
        public void onPageFinished(WebView view, String url) {
            switch (action) {
                case 0:
                    // fill field and submit
                    view.loadUrl("javascript:{\n" +
                            "\tdocument.querySelector('input[name=cgi_tel_no]').value='12345678';\n" +
                            "\tdocument.querySelector('input[name=cgi_id_no]').value='abcdef';\n" +
                            "\tsubmitMyForm();\n" +
                            "};");
                case 1:
                    // get content from 'ContentLoader' interface
                    view.loadUrl("javascript:{\n" +
                            "\twindow.ContentLoader.load('<body>'+document.body.innerHTML+'</body>');\t\n" +
                            "};");
                    break;
            }

            action++;
        }
    });
}

WebView createWebView() {
    WindowManager windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
    WindowManager.LayoutParams params = new WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY, WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, PixelFormat.TRANSLUCENT);
    params.gravity = Gravity.TOP | Gravity.LEFT;
    params.x = 0;
    params.y = 0;
    params.width = 0;
    params.height = 0;

    LinearLayout layout = new LinearLayout(this);
    layout.setLayoutParams(new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT));

    WebView view = new WebView(this);
    view.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT));
    layout.addView(view);

    windowManager.addView(layout, params);

    return view;
}
javax.net.ssl.SSLHandshakeException: Connection closed by peer in Android 5.0 Lollipop

Recently, there is a error occurs when access website via ssl connection like below although it worked fine several days ago.

// Enable SSL connection
HttpsURLConnection.setDefaultSSLSocketFactory(new MySocketFactory1());
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
    public boolean verify(String hostname, SSLSession session) {
        return true;
    }
});

URL address = new URL(url);
HttpsURLConnection urlConnection = (HttpsURLConnection) address.openConnection();
InputStream in = urlConnection.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[128];
int len = 0;
while ((len = in.read(buffer)) > 0) {
    baos.write(buffer, 0, len);
}
baos.close();
in.close();
MySocketFactory1.java
package com.prhythm.google.myapplication2.app;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

/**
 * Created by nanashi07 on 15/5/23.
 */
public class MySocketFactory1 extends SSLSocketFactory {

    SSLContext context;
    SSLSocketFactory factory;

    public MySocketFactory1() {
        try {
            init();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
    }

    void init() throws KeyManagementException, NoSuchAlgorithmException {
        context = SSLContext.getInstance("SSL");
        context.init(null, new X509TrustManager[]{new X509TrustManager() {
            public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }

            public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }

            public X509Certificate[] getAcceptedIssuers() {
                return new X509Certificate[0];
            }
        }}, new SecureRandom());
        factory = context.getSocketFactory();
    }

    @Override
    public String[] getDefaultCipherSuites() {
        return factory.getDefaultCipherSuites();
    }

    @Override
    public String[] getSupportedCipherSuites() {
        return factory.getSupportedCipherSuites();
    }

    @Override
    public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
        return factory.createSocket(s, host, port, autoClose);
    }

    @Override
    public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
        return factory.createSocket(host, port);
    }

    @Override
    public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException {
        return factory.createSocket(host, port, localHost, localPort);
    }

    @Override
    public Socket createSocket(InetAddress host, int port) throws IOException {
        return factory.createSocket(host, port);
    }

    @Override
    public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
        return factory.createSocket(address, port, localAddress, localPort);
    }
}
javax.net.ssl.SSLHandshakeException: Connection closed by peer
05-24 07:54:11.626    1697-1716/com.prhythm.google.myapplication2.app E/Test﹕ error
    javax.net.ssl.SSLHandshakeException: Connection closed by peer
            at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(Native Method)
            at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:302)
            at com.android.okhttp.Connection.upgradeToTls(Connection.java:197)
            at com.android.okhttp.Connection.connect(Connection.java:151)
            at com.android.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:276)
            at com.android.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:211)
            at com.android.okhttp.internal.http.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:373)
            at com.android.okhttp.internal.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:323)
            at com.android.okhttp.internal.http.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:190)
            at com.android.okhttp.internal.http.DelegatingHttpsURLConnection.getInputStream(DelegatingHttpsURLConnection.java:210)
            at com.android.okhttp.internal.http.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:25)
            at com.prhythm.google.myapplication2.app.MainActivity$1.doInBackground(MainActivity.java:41)
            at com.prhythm.google.myapplication2.app.MainActivity$1.doInBackground(MainActivity.java:26)
            at android.os.AsyncTask$2.call(AsyncTask.java:288)
            at java.util.concurrent.FutureTask.run(FutureTask.java:237)
            at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
            at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
            at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
            at java.lang.Thread.run(Thread.java:818)

After track and search from some technical articles, I found this TLS/SSL Default Configuration Changes, finally. Base on this, I've made some changes for my SSLSocketFactory to add cipher suites setting. Now it works fine again.

MySocketFactory2.java
package com.prhythm.google.myapplication2.app;

import com.prhythm.core.generic.util.Cube;

import javax.net.ssl.*;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Set;

/**
 * Created by nanashi07 on 15/5/23.
 */
public class MySocketFactory2 extends SSLSocketFactory {

    SSLContext context;
    SSLSocketFactory factory;
    String type;

    public MySocketFactory2() {
        try {
            init();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
    }

    void init() throws KeyManagementException, NoSuchAlgorithmException {
        context = SSLContext.getInstance(type = Cube.from("SSL", "TLS").random());
        context.init(null, new TrustManager[]{new X509TrustManager() {
            public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                System.out.printf("[CLIENT] chain = %s, authType = %s%n", Arrays.toString(chain), authType);
            }

            public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                System.out.printf("[SERVER] chain = %s, authType = %s%n", Arrays.toString(chain), authType);
            }

            public X509Certificate[] getAcceptedIssuers() {
                return new X509Certificate[0];
            }
        }}, new SecureRandom());
        factory = context.getSocketFactory();
    }

    @Override
    public String[] getDefaultCipherSuites() {
        return getCipherSuites().toArray(String.class);
    }

    @Override
    public String[] getSupportedCipherSuites() {
        return getCipherSuites().toArray(String.class);
    }

    @Override
    public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
        return configCipherSuites(factory.createSocket(s, host, port, autoClose));
    }

    @Override
    public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
        return configCipherSuites(factory.createSocket(host, port));
    }

    @Override
    public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException {
        return configCipherSuites(factory.createSocket(host, port, localHost, localPort));
    }

    @Override
    public Socket createSocket(InetAddress host, int port) throws IOException {
        return configCipherSuites(factory.createSocket(host, port));
    }

    @Override
    public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
        return configCipherSuites(factory.createSocket(address, port, localAddress, localPort));
    }

    Cube<String> getCipherSuites() {
        return Cube.from("SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA",
                "SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA",
                "SSL_DHE_DSS_WITH_DES_CBC_SHA",
                "SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA",
                "SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA",
                "SSL_DHE_RSA_WITH_DES_CBC_SHA",
                "SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA",
                "SSL_DH_anon_EXPORT_WITH_RC4_40_MD5",
                "SSL_DH_anon_WITH_3DES_EDE_CBC_SHA",
                "SSL_DH_anon_WITH_DES_CBC_SHA",
                "SSL_DH_anon_WITH_RC4_128_MD5",
                "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA",
                "SSL_RSA_EXPORT_WITH_RC4_40_MD5",
                "SSL_RSA_WITH_3DES_EDE_CBC_SHA",
                "SSL_RSA_WITH_DES_CBC_SHA",
                "SSL_RSA_WITH_NULL_MD5",
                "SSL_RSA_WITH_NULL_SHA",
                "SSL_RSA_WITH_RC4_128_MD5",
                "SSL_RSA_WITH_RC4_128_SHA",
                "TLS_DHE_DSS_WITH_AES_128_CBC_SHA",
                "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256",
                "TLS_DHE_DSS_WITH_AES_128_GCM_SHA256",
                "TLS_DHE_DSS_WITH_AES_256_CBC_SHA",
                "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256",
                "TLS_DHE_DSS_WITH_AES_256_GCM_SHA384",
                "TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
                "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256",
                "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",
                "TLS_DHE_RSA_WITH_AES_256_CBC_SHA",
                "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256",
                "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384",
                "TLS_DH_anon_WITH_AES_128_CBC_SHA",
                "TLS_DH_anon_WITH_AES_128_CBC_SHA256",
                "TLS_DH_anon_WITH_AES_128_GCM_SHA256",
                "TLS_DH_anon_WITH_AES_256_CBC_SHA",
                "TLS_DH_anon_WITH_AES_256_CBC_SHA256",
                "TLS_DH_anon_WITH_AES_256_GCM_SHA384",
                "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA",
                "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
                "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
                "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
                "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
                "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
                "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
                "TLS_ECDHE_ECDSA_WITH_NULL_SHA",
                "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
                "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA",
                "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA",
                "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
                "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
                "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
                "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
                "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
                "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
                "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
                "TLS_ECDHE_RSA_WITH_NULL_SHA",
                "TLS_ECDHE_RSA_WITH_RC4_128_SHA",
                "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA",
                "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA",
                "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256",
                "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256",
                "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA",
                "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384",
                "TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384",
                "TLS_ECDH_ECDSA_WITH_NULL_SHA",
                "TLS_ECDH_ECDSA_WITH_RC4_128_SHA",
                "TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA",
                "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA",
                "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256",
                "TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256",
                "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA",
                "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384",
                "TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384",
                "TLS_ECDH_RSA_WITH_NULL_SHA",
                "TLS_ECDH_RSA_WITH_RC4_128_SHA",
                "TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA",
                "TLS_ECDH_anon_WITH_AES_128_CBC_SHA",
                "TLS_ECDH_anon_WITH_AES_256_CBC_SHA",
                "TLS_ECDH_anon_WITH_NULL_SHA",
                "TLS_ECDH_anon_WITH_RC4_128_SHA",
                "TLS_EMPTY_RENEGOTIATION_INFO_SCSV",
                "TLS_FALLBACK_SCSV",
                "TLS_PSK_WITH_3DES_EDE_CBC_SHA",
                "TLS_PSK_WITH_AES_128_CBC_SHA",
                "TLS_PSK_WITH_AES_256_CBC_SHA",
                "TLS_PSK_WITH_RC4_128_SHA",
                "TLS_RSA_WITH_AES_128_CBC_SHA",
                "TLS_RSA_WITH_AES_128_CBC_SHA256",
                "TLS_RSA_WITH_AES_128_GCM_SHA256",
                "TLS_RSA_WITH_AES_256_CBC_SHA",
                "TLS_RSA_WITH_AES_256_CBC_SHA256",
                "TLS_RSA_WITH_AES_256_GCM_SHA384",
                "TLS_RSA_WITH_NULL_SHA256")
                .where(new Cube.Selection<String>() {
                    @Override
                    public boolean predicate(String item, int index) {
                        return item.startsWith(type);
                    }
                });
    }

    Socket configCipherSuites(Socket socket) {
        if (!(socket instanceof SSLSocket)) return socket;
        SSLSocket sslsocket = (SSLSocket) socket;
        // 處理不合的cipher suite
        Set<String> suites = getCipherSuites().toSet();
        while (suites.size() > 0) {
            try {
                sslsocket.setEnabledCipherSuites(suites.toArray(new String[suites.size()]));
                break;
            } catch (Throwable e) {
                String message = e.getMessage();
                for (String cipher : suites) {
                    if (message.toLowerCase().contains(cipher.toLowerCase())) {
                        suites.remove(cipher);
                        System.out.printf("Cipher suite %s has been removed.%n", cipher);
                        break;
                    }
                }
            }
        }
        return socket;
    }
}
android.intent.action.SCREEN_ON & android.intent.action.SCREEN_OFF

First, I've tried create a receiver to receive screen on/off and register receiver on AndroidManifest.xml like below, but unfortunately it does not work.

ScreenReceiver.java
package com.prhythm.training.service;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

/**
 * Created by Bruce on 7/15/2014.
 */
public class ScreenReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d("Receiver", String.format("Broadcast action: %s", intent.getAction()));
    }
}
AndroidManifest.xml
<receiver android:name=".service.ScreenReceiver">
    <intent-filter>
        <action android:name="android.intent.action.SCREEN_ON"/>
        <action android:name="android.intent.action.SCREEN_OFF"/>
    </intent-filter>
</receiver>

I's nessary to register screen action receiver by code, so I've change the as

ScreenReceiver.java
package com.prhythm.training.service;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

/**
 * Created by Bruce on 7/15/2014.
 */
public class ScreenReceiver extends BroadcastReceiver {

    public static ScreenReceiver CURRENT;

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d("Receiver", String.format("Broadcast action: %s", intent.getAction()));
    }
}
ScreenService.java
package com.prhythm.training.service;

import android.app.Service;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.IBinder;

/**
 * Created by Bruce on 7/15/2014.
 */
public class ScreenService extends Service {
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        // Register screen on/off receiver
        if (ScreenReceiver.CURRENT == null) ScreenReceiver.CURRENT = new ScreenReceiver();
        getApplicationContext().registerReceiver(ScreenReceiver.CURRENT, new IntentFilter(Intent.ACTION_SCREEN_ON));
        getApplicationContext().registerReceiver(ScreenReceiver.CURRENT, new IntentFilter(Intent.ACTION_SCREEN_OFF));
    }
}

Then, start this service is required

startService(new Intent(this, ScreenService.class));
Register preference change event with PreferenceFragment
public class DevicePreferenceFragment extends PreferenceFragment implements SharedPreferences.OnSharedPreferenceChangeListener {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.xml.preference);
    }

    @Override
    public void onResume() {
        super.onResume();
        getPreferenceManager().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);

    }

    @Override
    public void onPause() {
        getPreferenceManager().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
        super.onPause();
    }

    @Override
    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
        Log.d(MainActivity.TAG, String.format("Preference '%s' has been changed.", key));
        if ("pref_track_interval".equals(key)) {
            // do something
        }
    }
}