lyhzzz 2 年之前
父節點
當前提交
953da097c4
共有 32 個文件被更改,包括 2164 次插入13 次删除
  1. 1 5
      pom.xml
  2. 61 0
      src/main/java/com/fdkankan/tk/io/agora/chat/ChatTokenBuilder2.java
  3. 99 0
      src/main/java/com/fdkankan/tk/io/agora/education/EducationTokenBuilder2.java
  4. 169 0
      src/main/java/com/fdkankan/tk/io/agora/media/AccessToken.java
  5. 380 0
      src/main/java/com/fdkankan/tk/io/agora/media/AccessToken2.java
  6. 125 0
      src/main/java/com/fdkankan/tk/io/agora/media/ByteBuf.java
  7. 37 0
      src/main/java/com/fdkankan/tk/io/agora/media/DynamicKey.java
  8. 40 0
      src/main/java/com/fdkankan/tk/io/agora/media/DynamicKey3.java
  9. 82 0
      src/main/java/com/fdkankan/tk/io/agora/media/DynamicKey4.java
  10. 149 0
      src/main/java/com/fdkankan/tk/io/agora/media/DynamicKey5.java
  11. 33 0
      src/main/java/com/fdkankan/tk/io/agora/media/DynamicKeyUtil.java
  12. 27 0
      src/main/java/com/fdkankan/tk/io/agora/media/FpaTokenBuilder.java
  13. 8 0
      src/main/java/com/fdkankan/tk/io/agora/media/Packable.java
  14. 5 0
      src/main/java/com/fdkankan/tk/io/agora/media/PackableEx.java
  15. 108 0
      src/main/java/com/fdkankan/tk/io/agora/media/RtcTokenBuilder.java
  16. 203 0
      src/main/java/com/fdkankan/tk/io/agora/media/RtcTokenBuilder2.java
  17. 140 0
      src/main/java/com/fdkankan/tk/io/agora/media/Utils.java
  18. 33 0
      src/main/java/com/fdkankan/tk/io/agora/rtm/RtmTokenBuilder.java
  19. 34 0
      src/main/java/com/fdkankan/tk/io/agora/rtm/RtmTokenBuilder2.java
  20. 93 0
      src/main/java/com/fdkankan/tk/io/agora/sample/AccessTokenInspector.java
  21. 19 0
      src/main/java/com/fdkankan/tk/io/agora/sample/ChatTokenBuilder2Sample.java
  22. 27 0
      src/main/java/com/fdkankan/tk/io/agora/sample/DynamicKey5Sample.java
  23. 25 0
      src/main/java/com/fdkankan/tk/io/agora/sample/EducationTokenBuilder2Sample.java
  24. 15 0
      src/main/java/com/fdkankan/tk/io/agora/sample/FpaTokenBuilderSample.java
  25. 29 0
      src/main/java/com/fdkankan/tk/io/agora/sample/RtcTokenBuilder2Sample.java
  26. 25 0
      src/main/java/com/fdkankan/tk/io/agora/sample/RtcTokenBuilderSample.java
  27. 17 0
      src/main/java/com/fdkankan/tk/io/agora/sample/RtmTokenBuilder2Sample.java
  28. 17 0
      src/main/java/com/fdkankan/tk/io/agora/sample/RtmTokenBuilderSample.java
  29. 22 0
      src/main/java/com/fdkankan/tk/io/agora/sample/SignalingTokenSample.java
  30. 90 0
      src/main/java/com/fdkankan/tk/io/agora/sample/Verifier5.java
  31. 34 0
      src/main/java/com/fdkankan/tk/io/agora/signal/SignalingToken.java
  32. 17 8
      src/main/java/com/fdkankan/tk/service/impl/TencentYunServiceImpl.java

+ 1 - 5
pom.xml

@@ -149,11 +149,7 @@
         </dependency>
         </dependency>
 
 
 
 
-        <dependency>
-            <groupId>io.agora</groupId>
-            <artifactId>authentication</artifactId>
-            <version>1.6.1</version>
-        </dependency>
+
 
 
 
 
     </dependencies>
     </dependencies>

+ 61 - 0
src/main/java/com/fdkankan/tk/io/agora/chat/ChatTokenBuilder2.java

@@ -0,0 +1,61 @@
+package com.fdkankan.tk.io.agora.chat;
+
+
+import com.fdkankan.tk.io.agora.media.AccessToken2;
+
+public class ChatTokenBuilder2 {
+
+    /**
+     * Build the CHAT user token.
+     *
+     * @param appId:          The App ID issued to you by Agora. Apply for a new App ID from
+     *                        Agora Dashboard if it is missing from your kit. See Get an App ID.
+     * @param appCertificate: Certificate of the application that you registered in
+     *                        the Agora Dashboard. See Get an App Certificate.
+     * @param userId:         The user's id, must be unique.
+     *                        optionalUid must be unique.
+     * @param expire:         represented by the number of seconds elapsed since now. If, for example, you want to access the
+     *                        Agora Service within 10 minutes after the token is generated, set expireTimestamp as 600(seconds).
+     * @return The RTC token.
+     */
+    public String buildUserToken(String appId, String appCertificate, String userId, int expire) {
+        AccessToken2 accessToken = new AccessToken2(appId, appCertificate, expire);
+        AccessToken2.Service serviceChat = new AccessToken2.ServiceChat(userId);
+
+        serviceChat.addPrivilegeChat(AccessToken2.PrivilegeChat.PRIVILEGE_CHAT_USER, expire);
+        accessToken.addService(serviceChat);
+
+        try {
+            return accessToken.build();
+        } catch (Exception e) {
+            e.printStackTrace();
+            return "";
+        }
+    }
+
+    /**
+     * Build the CHAT app token.
+     *
+     * @param appId:          The App ID issued to you by Agora. Apply for a new App ID from
+     *                        Agora Dashboard if it is missing from your kit. See Get an App ID.
+     * @param appCertificate: Certificate of the application that you registered in
+     *                        the Agora Dashboard. See Get an App Certificate.
+     * @param expire:         represented by the number of seconds elapsed since now. If, for example, you want to access the
+     *                        Agora Service within 10 minutes after the token is generated, set expireTimestamp as 600(seconds).
+     * @return The RTC token.
+     */
+    public String buildAppToken(String appId, String appCertificate, int expire) {
+        AccessToken2 accessToken = new AccessToken2(appId, appCertificate, expire);
+        AccessToken2.Service serviceChat = new AccessToken2.ServiceChat();
+
+        serviceChat.addPrivilegeChat(AccessToken2.PrivilegeChat.PRIVILEGE_CHAT_APP, expire);
+        accessToken.addService(serviceChat);
+
+        try {
+            return accessToken.build();
+        } catch (Exception e) {
+            e.printStackTrace();
+            return "";
+        }
+    }
+}

+ 99 - 0
src/main/java/com/fdkankan/tk/io/agora/education/EducationTokenBuilder2.java

@@ -0,0 +1,99 @@
+package com.fdkankan.tk.io.agora.education;
+
+
+import com.fdkankan.tk.io.agora.media.AccessToken2;
+import com.fdkankan.tk.io.agora.media.Utils;
+
+public class EducationTokenBuilder2 {
+
+    /**
+     * build user room token
+     *
+     * @param appId          The App ID issued to you by Agora. Apply for a new App ID from
+     *                       Agora Dashboard if it is missing from your kit. See Get an App ID.
+     * @param appCertificate Certificate of the application that you registered in
+     *                       the Agora Dashboard. See Get an App Certificate.
+     * @param roomUuid       The room's id, must be unique.
+     * @param userUuid       The user's id, must be unique.
+     * @param role           The user's role, such as 0(invisible), 1(teacher), 2(student), 3(assistant), 4(observer) etc.
+     * @param expire         represented by the number of seconds elapsed since now. If, for example, you want to access the
+     *                       Agora Service within 10 minutes after the token is generated, set expireTimestamp as 600(seconds).
+     * @return The education user room token.
+     */
+    public String buildRoomUserToken(String appId, String appCertificate, String roomUuid, String userUuid, Short role, int expire) {
+        AccessToken2 accessToken = new AccessToken2(appId, appCertificate, expire);
+        String chatUserId = Utils.md5(userUuid);
+
+        AccessToken2.Service serviceEducation = new AccessToken2.ServiceEducation(roomUuid, userUuid, role);
+        serviceEducation.addPrivilegeEducation(AccessToken2.PrivilegeEducation.PRIVILEGE_ROOM_USER, expire);
+        accessToken.addService(serviceEducation);
+
+        AccessToken2.Service serviceRtm = new AccessToken2.ServiceRtm(userUuid);
+        serviceRtm.addPrivilegeRtm(AccessToken2.PrivilegeRtm.PRIVILEGE_LOGIN, expire);
+        accessToken.addService(serviceRtm);
+
+        AccessToken2.Service serviceChat = new AccessToken2.ServiceChat(chatUserId);
+        serviceRtm.addPrivilegeChat(AccessToken2.PrivilegeChat.PRIVILEGE_CHAT_USER, expire);
+        accessToken.addService(serviceChat);
+
+        try {
+            return accessToken.build();
+        } catch (Exception e) {
+            e.printStackTrace();
+            return "";
+        }
+    }
+
+    /**
+     * build user individual token
+     *
+     * @param appId          The App ID issued to you by Agora. Apply for a new App ID from
+     *                       Agora Dashboard if it is missing from your kit. See Get an App ID.
+     * @param appCertificate Certificate of the application that you registered in
+     *                       the Agora Dashboard. See Get an App Certificate.
+     * @param userUuid       The user's id, must be unique.
+     * @param expire         represented by the number of seconds elapsed since now. If, for example, you want to access the
+     *                       Agora Service within 10 minutes after the token is generated, set expireTimestamp as 600(seconds).
+     * @return The education user token.
+     */
+    public String buildUserToken(String appId, String appCertificate, String userUuid, int expire) {
+        AccessToken2 accessToken = new AccessToken2(appId, appCertificate, expire);
+        AccessToken2.Service service = new AccessToken2.ServiceEducation(userUuid);
+
+        service.addPrivilegeEducation(AccessToken2.PrivilegeEducation.PRIVILEGE_USER, expire);
+        accessToken.addService(service);
+
+        try {
+            return accessToken.build();
+        } catch (Exception e) {
+            e.printStackTrace();
+            return "";
+        }
+    }
+
+    /**
+     * build app global token
+     *
+     * @param appId          The App ID issued to you by Agora. Apply for a new App ID from
+     *                       Agora Dashboard if it is missing from your kit. See Get an App ID.
+     * @param appCertificate Certificate of the application that you registered in
+     *                       the Agora Dashboard. See Get an App Certificate.
+     * @param expire         represented by the number of seconds elapsed since now. If, for example, you want to access the
+     *                       Agora Service within 10 minutes after the token is generated, set expireTimestamp as 600(seconds).
+     * @return The education global token.
+     */
+    public String buildAppToken(String appId, String appCertificate, int expire) {
+        AccessToken2 accessToken = new AccessToken2(appId, appCertificate, expire);
+        AccessToken2.Service serviceEducation = new AccessToken2.ServiceEducation();
+
+        serviceEducation.addPrivilegeEducation(AccessToken2.PrivilegeEducation.PRIVILEGE_APP, expire);
+        accessToken.addService(serviceEducation);
+
+        try {
+            return accessToken.build();
+        } catch (Exception e) {
+            e.printStackTrace();
+            return "";
+        }
+    }
+}

+ 169 - 0
src/main/java/com/fdkankan/tk/io/agora/media/AccessToken.java

@@ -0,0 +1,169 @@
+package com.fdkankan.tk.io.agora.media;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.TreeMap;
+
+import static com.fdkankan.tk.io.agora.media.Utils.crc32;
+
+
+public class AccessToken {
+    public enum Privileges {
+        kJoinChannel(1),
+        kPublishAudioStream(2),
+        kPublishVideoStream(3),
+        kPublishDataStream(4),
+        
+        // For RTM only
+        kRtmLogin(1000);
+    	
+        public short intValue;
+
+        Privileges(int value) {
+            intValue = (short) value;
+        }
+    }
+
+    private static final String VER = "006";
+    
+    public String appId;
+    public String appCertificate;
+    public String channelName;
+    public String uid;
+    public byte[] signature;
+    public byte[] messageRawContent;
+    public int crcChannelName;
+    public int crcUid;
+    public PrivilegeMessage message;
+    public int expireTimestamp;
+
+    public AccessToken(String appId, String appCertificate, String channelName, String uid) {
+        this.appId = appId;
+        this.appCertificate = appCertificate;
+        this.channelName = channelName;
+        this.uid = uid;
+        this.crcChannelName = 0;
+        this.crcUid = 0;
+        this.message = new PrivilegeMessage();
+    }
+
+    public String build() throws Exception {
+        if (! Utils.isUUID(appId)) {
+            return "";
+        }
+
+        if (!Utils.isUUID(appCertificate)) {
+            return "";
+        }
+
+        messageRawContent = Utils.pack(message);
+        signature = generateSignature(appCertificate, 
+        		appId, channelName, uid, messageRawContent);
+        crcChannelName = crc32(channelName);
+        crcUid = crc32(uid);
+
+        PackContent packContent = new PackContent(signature, crcChannelName, crcUid, messageRawContent);
+        byte[] content = Utils.pack(packContent);
+        return getVersion() + this.appId + Utils.base64Encode(content);
+    }
+
+    public void addPrivilege(Privileges privilege, int expireTimestamp) {
+        message.messages.put(privilege.intValue, expireTimestamp);
+    }
+
+    public static String getVersion() {
+        return VER;
+    }
+
+    public static byte[] generateSignature(String appCertificate, 
+    		String appID, String channelName, String uid, byte[] message) throws Exception {
+    	
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        try {
+            baos.write(appID.getBytes());
+            baos.write(channelName.getBytes());
+            baos.write(uid.getBytes());
+            baos.write(message);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return Utils.hmacSign(appCertificate, baos.toByteArray());
+    }
+
+    public boolean fromString(String token) {
+        if (!getVersion().equals(token.substring(0, Utils.VERSION_LENGTH))) {
+            return false;
+        }
+        
+        try {
+            appId = token.substring(Utils.VERSION_LENGTH, Utils.VERSION_LENGTH + Utils.APP_ID_LENGTH);
+            PackContent packContent = new PackContent();
+            Utils.unpack(Utils.base64Decode(token.substring(Utils.VERSION_LENGTH + Utils.APP_ID_LENGTH, token.length())), packContent);
+            signature = packContent.signature;
+            crcChannelName = packContent.crcChannelName;
+            crcUid = packContent.crcUid;
+            messageRawContent = packContent.rawMessage;
+            Utils.unpack(messageRawContent, message);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+        
+        return true;
+    }
+
+    public class PrivilegeMessage implements PackableEx {
+        public int salt;
+        public int ts;
+        public TreeMap<Short, Integer> messages;
+
+        public PrivilegeMessage() {
+            salt = Utils.randomInt();
+            ts = Utils.getTimestamp() + 24 * 3600;
+            messages = new TreeMap<>();
+        }
+
+        @Override
+        public ByteBuf marshal(ByteBuf out) {
+            return out.put(salt).put(ts).putIntMap(messages);
+        }
+
+        @Override
+        public void unmarshal(ByteBuf in) {
+            salt = in.readInt();
+            ts = in.readInt();
+            messages = in.readIntMap();
+        }
+    }
+
+    public class PackContent implements PackableEx {
+        public byte[] signature;
+        public int crcChannelName;
+        public int crcUid;
+        public byte[] rawMessage;
+
+        public PackContent() {
+        	// Nothing done
+        }
+        
+        public PackContent(byte[] signature, int crcChannelName, int crcUid, byte[] rawMessage) {
+            this.signature = signature;
+            this.crcChannelName = crcChannelName;
+            this.crcUid = crcUid;
+            this.rawMessage = rawMessage;
+        }
+
+        @Override
+        public ByteBuf marshal(ByteBuf out) {
+            return out.put(signature).put(crcChannelName).put(crcUid).put(rawMessage);
+        }
+
+        @Override
+        public void unmarshal(ByteBuf in) {
+            signature = in.readBytes();
+            crcChannelName = in.readInt();
+            crcUid = in.readInt();
+            rawMessage = in.readBytes();
+        }
+    }
+}

+ 380 - 0
src/main/java/com/fdkankan/tk/io/agora/media/AccessToken2.java

@@ -0,0 +1,380 @@
+package com.fdkankan.tk.io.agora.media;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+import java.util.Map;
+import java.util.TreeMap;
+
+public class AccessToken2 {
+    public enum PrivilegeRtc {
+        PRIVILEGE_JOIN_CHANNEL(1),
+        PRIVILEGE_PUBLISH_AUDIO_STREAM(2),
+        PRIVILEGE_PUBLISH_VIDEO_STREAM(3),
+        PRIVILEGE_PUBLISH_DATA_STREAM(4),
+        ;
+
+        public short intValue;
+
+        PrivilegeRtc(int value) {
+            intValue = (short) value;
+        }
+    }
+
+    public enum PrivilegeRtm {
+        PRIVILEGE_LOGIN(1),
+        ;
+
+        public short intValue;
+
+        PrivilegeRtm(int value) {
+            intValue = (short) value;
+        }
+    }
+
+    public enum PrivilegeFpa {
+        PRIVILEGE_LOGIN(1),
+        ;
+
+        public short intValue;
+
+        PrivilegeFpa(int value) {
+            intValue = (short) value;
+        }
+    }
+
+    public enum PrivilegeChat {
+        PRIVILEGE_CHAT_USER(1),
+        PRIVILEGE_CHAT_APP(2),
+        ;
+
+        public short intValue;
+
+        PrivilegeChat(int value) {
+            intValue = (short) value;
+        }
+    }
+
+    public enum PrivilegeEducation {
+        PRIVILEGE_ROOM_USER(1),
+        PRIVILEGE_USER(2),
+        PRIVILEGE_APP(3),
+        ;
+
+        public short intValue;
+
+        PrivilegeEducation(int value) {
+            intValue = (short) value;
+        }
+    }
+
+    private static final String VERSION = "007";
+    public static final short SERVICE_TYPE_RTC = 1;
+    public static final short SERVICE_TYPE_RTM = 2;
+    public static final short SERVICE_TYPE_FPA = 4;
+    public static final short SERVICE_TYPE_CHAT = 5;
+    public static final short SERVICE_TYPE_EDUCATION = 7;
+
+    public String appCert = "";
+    public String appId = "";
+    public int expire;
+    public int issueTs;
+    public int salt;
+    public Map<Short, Service> services = new TreeMap<>();
+
+    public AccessToken2() {
+    }
+
+    public AccessToken2(String appId, String appCert, int expire) {
+        this.appCert = appCert;
+        this.appId = appId;
+        this.expire = expire;
+        this.issueTs = Utils.getTimestamp();
+        this.salt = Utils.randomInt();
+    }
+
+    public void addService(Service service) {
+        this.services.put(service.getServiceType(), service);
+    }
+
+    public String build() throws Exception {
+        if (!Utils.isUUID(this.appId) || !Utils.isUUID(this.appCert)) {
+            return "";
+        }
+
+        ByteBuf buf = new ByteBuf().put(this.appId).put(this.issueTs).put(this.expire).put(this.salt).put((short) this.services.size());
+        byte[] signing = getSign();
+
+        this.services.forEach((k, v) -> {
+            v.pack(buf);
+        });
+
+        Mac mac = Mac.getInstance("HmacSHA256");
+        mac.init(new SecretKeySpec(signing, "HmacSHA256"));
+        byte[] signature = mac.doFinal(buf.asBytes());
+
+        ByteBuf bufferContent = new ByteBuf();
+        bufferContent.put(signature);
+        bufferContent.buffer.put(buf.asBytes());
+
+        return getVersion() + Utils.base64Encode(Utils.compress(bufferContent.asBytes()));
+    }
+
+    public Service getService(short serviceType) {
+        if (serviceType == SERVICE_TYPE_RTC) {
+            return new ServiceRtc();
+        }
+        if (serviceType == SERVICE_TYPE_RTM) {
+            return new ServiceRtm();
+        }
+        if (serviceType == SERVICE_TYPE_FPA) {
+            return new ServiceFpa();
+        }
+        if (serviceType == SERVICE_TYPE_CHAT) {
+            return new ServiceChat();
+        }
+        if (serviceType == SERVICE_TYPE_EDUCATION) {
+            return new ServiceEducation();
+        }
+        throw new IllegalArgumentException(String.format("unknown service type: `%d`", serviceType));
+    }
+
+    public byte[] getSign() throws Exception {
+        Mac mac = Mac.getInstance("HmacSHA256");
+        mac.init(new SecretKeySpec(new ByteBuf().put(this.issueTs).asBytes(), "HmacSHA256"));
+        byte[] signing = mac.doFinal(this.appCert.getBytes());
+        mac.init(new SecretKeySpec(new ByteBuf().put(this.salt).asBytes(), "HmacSHA256"));
+        return mac.doFinal(signing);
+    }
+
+    public static String getUidStr(int uid) {
+        if (uid == 0) {
+            return "";
+        }
+        return String.valueOf(uid & 0xFFFFFFFFL);
+    }
+
+    public static String getVersion() {
+        return VERSION;
+    }
+
+    public boolean parse(String token) {
+        if (!getVersion().equals(token.substring(0, Utils.VERSION_LENGTH))) {
+            return false;
+        }
+        byte[] data = Utils.decompress(Utils.base64Decode(token.substring(Utils.VERSION_LENGTH)));
+        ByteBuf buff = new ByteBuf(data);
+        String signature = buff.readString();
+        this.appId = buff.readString();
+        this.issueTs = buff.readInt();
+        this.expire = buff.readInt();
+        this.salt = buff.readInt();
+        short servicesNum = buff.readShort();
+
+        for (int i = 0; i < servicesNum; i++) {
+            short serviceType = buff.readShort();
+            Service service = getService(serviceType);
+            service.unpack(buff);
+            this.services.put(serviceType, service);
+        }
+        return true;
+    }
+
+    public static class Service {
+        public short type;
+        public TreeMap<Short, Integer> privileges = new TreeMap<Short, Integer>() {
+        };
+
+        public Service() {
+        }
+
+        public Service(short serviceType) {
+            this.type = serviceType;
+        }
+
+        public void addPrivilegeRtc(PrivilegeRtc privilege, int expire) {
+            this.privileges.put(privilege.intValue, expire);
+        }
+
+        public void addPrivilegeRtm(PrivilegeRtm privilege, int expire) {
+            this.privileges.put(privilege.intValue, expire);
+        }
+
+        public void addPrivilegeFpa(PrivilegeFpa privilege, int expire) {
+            this.privileges.put(privilege.intValue, expire);
+        }
+
+        public void addPrivilegeChat(PrivilegeChat privilege, int expire) {
+            this.privileges.put(privilege.intValue, expire);
+        }
+
+        public void addPrivilegeEducation(PrivilegeEducation privilege, int expire) {
+            this.privileges.put(privilege.intValue, expire);
+        }
+
+        public TreeMap<Short, Integer> getPrivileges() {
+            return this.privileges;
+        }
+
+        public short getServiceType() {
+            return this.type;
+        }
+
+        public ByteBuf pack(ByteBuf buf) {
+            return buf.put(this.type).putIntMap(this.privileges);
+        }
+
+        public void unpack(ByteBuf byteBuf) {
+            this.privileges = byteBuf.readIntMap();
+        }
+    }
+
+    public static class ServiceRtc extends Service {
+        public String channelName;
+        public String uid;
+
+        public ServiceRtc() {
+            this.type = SERVICE_TYPE_RTC;
+        }
+
+        public ServiceRtc(String channelName, String uid) {
+            this.type = SERVICE_TYPE_RTC;
+            this.channelName = channelName;
+            this.uid = uid;
+        }
+
+        public String getChannelName() {
+            return this.channelName;
+        }
+
+        public String getUid() {
+            return this.uid;
+        }
+
+        public ByteBuf pack(ByteBuf buf) {
+            return super.pack(buf).put(this.channelName).put(this.uid);
+        }
+
+        public void unpack(ByteBuf byteBuf) {
+            super.unpack(byteBuf);
+            this.channelName = byteBuf.readString();
+            this.uid = byteBuf.readString();
+        }
+    }
+
+    public static class ServiceRtm extends Service {
+        public String userId;
+
+        public ServiceRtm() {
+            this.type = SERVICE_TYPE_RTM;
+        }
+
+        public ServiceRtm(String userId) {
+            this.type = SERVICE_TYPE_RTM;
+            this.userId = userId;
+        }
+
+        public String getUserId() {
+            return this.userId;
+        }
+
+        public ByteBuf pack(ByteBuf buf) {
+            return super.pack(buf).put(this.userId);
+        }
+
+        public void unpack(ByteBuf byteBuf) {
+            super.unpack(byteBuf);
+            this.userId = byteBuf.readString();
+        }
+    }
+
+    public static class ServiceFpa extends Service {
+        public ServiceFpa() {
+            this.type = SERVICE_TYPE_FPA;
+        }
+
+        public ByteBuf pack(ByteBuf buf) {
+            return super.pack(buf);
+        }
+
+        public void unpack(ByteBuf byteBuf) {
+            super.unpack(byteBuf);
+        }
+    }
+
+    public static class ServiceChat extends Service {
+        public String userId;
+
+        public ServiceChat() {
+            this.type = SERVICE_TYPE_CHAT;
+            this.userId = "";
+        }
+
+        public ServiceChat(String userId) {
+            this.type = SERVICE_TYPE_CHAT;
+            this.userId = userId;
+        }
+
+        public String getUserId() {
+            return this.userId;
+        }
+
+        public ByteBuf pack(ByteBuf buf) {
+            return super.pack(buf).put(this.userId);
+        }
+
+        public void unpack(ByteBuf byteBuf) {
+            super.unpack(byteBuf);
+            this.userId = byteBuf.readString();
+        }
+    }
+
+    public static class ServiceEducation extends Service {
+        public String roomUuid;
+        public String userUuid;
+        public Short role;
+
+        public ServiceEducation() {
+            this.type = SERVICE_TYPE_EDUCATION;
+            this.roomUuid = "";
+            this.userUuid = "";
+            this.role = -1;
+        }
+
+        public ServiceEducation(String roomUuid, String userUuid, Short role) {
+            this.type = SERVICE_TYPE_EDUCATION;
+            this.roomUuid = roomUuid;
+            this.userUuid = userUuid;
+            this.role = role;
+        }
+
+        public ServiceEducation(String userUuid) {
+            this.type = SERVICE_TYPE_EDUCATION;
+            this.roomUuid = "";
+            this.userUuid = userUuid;
+            this.role = -1;
+        }
+
+        public String getRoomUuid() {
+            return this.roomUuid;
+        }
+
+        public String getUserUuid() {
+            return this.userUuid;
+        }
+
+        public Short getRole() {
+            return this.role;
+        }
+
+        public ByteBuf pack(ByteBuf buf) {
+            return super.pack(buf).put(this.roomUuid).put(this.userUuid).put(this.role);
+        }
+
+        public void unpack(ByteBuf byteBuf) {
+            super.unpack(byteBuf);
+            this.roomUuid = byteBuf.readString();
+            this.userUuid = byteBuf.readString();
+            this.role = byteBuf.readShort();
+        }
+    }
+}

+ 125 - 0
src/main/java/com/fdkankan/tk/io/agora/media/ByteBuf.java

@@ -0,0 +1,125 @@
+package com.fdkankan.tk.io.agora.media;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * Created by Li on 10/1/2016.
+ */
+public class ByteBuf {
+    ByteBuffer buffer = ByteBuffer.allocate(1024).order(ByteOrder.LITTLE_ENDIAN);
+
+    public ByteBuf() {
+    }
+
+    public ByteBuf(byte[] bytes) {
+        this.buffer = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN);
+    }
+
+    public byte[] asBytes() {
+        byte[] out = new byte[buffer.position()];
+        buffer.rewind();
+        buffer.get(out, 0, out.length);
+        return out;
+    }
+
+    // packUint16
+    public ByteBuf put(short v) {
+        buffer.putShort(v);
+        return this;
+    }
+
+    public ByteBuf put(byte[] v) {
+        put((short)v.length);
+        buffer.put(v);
+        return this;
+    }
+
+    // packUint32
+    public ByteBuf put(int v) {
+        buffer.putInt(v);
+        return this;
+    }
+
+    public ByteBuf put(long v) {
+        buffer.putLong(v);
+        return this;
+    }
+
+    public ByteBuf put(String v) {
+        return put(v.getBytes());
+    }
+
+    public ByteBuf put(TreeMap<Short, String> extra) {
+        put((short)extra.size());
+
+        for (Map.Entry<Short, String> pair : extra.entrySet()) {
+            put(pair.getKey());
+            put(pair.getValue());
+        }
+
+        return this;
+    }
+
+    public ByteBuf putIntMap(TreeMap<Short, Integer> extra) {
+        put((short)extra.size());
+
+        for (Map.Entry<Short, Integer> pair : extra.entrySet()) {
+            put(pair.getKey());
+            put(pair.getValue());
+        }
+
+        return this;
+    }
+
+    public short readShort() {
+        return buffer.getShort();
+    }
+
+
+    public int readInt() {
+        return buffer.getInt();
+    }
+
+    public byte[] readBytes() {
+        short length = readShort();
+        byte[] bytes = new byte[length];
+        buffer.get(bytes);
+        return bytes;
+    }
+
+    public String readString() {
+        byte[] bytes = readBytes();
+        return new String(bytes);
+    }
+
+    public TreeMap readMap() {
+        TreeMap<Short, String> map = new TreeMap<>();
+
+        short length = readShort();
+
+        for (short i = 0; i < length; ++i) {
+            short k = readShort();
+            String v = readString();
+            map.put(k, v);
+        }
+
+        return map;
+    }
+
+    public TreeMap<Short, Integer> readIntMap() {
+        TreeMap<Short, Integer> map = new TreeMap<>();
+
+        short length = readShort();
+
+        for (short i = 0; i < length; ++i) {
+            short k = readShort();
+            Integer v = readInt();
+            map.put(k, v);
+        }
+
+        return map;
+    }
+}

+ 37 - 0
src/main/java/com/fdkankan/tk/io/agora/media/DynamicKey.java

@@ -0,0 +1,37 @@
+package com.fdkankan.tk.io.agora.media;
+
+import java.io.ByteArrayOutputStream;
+
+/**
+ * Created by hefeng on 15/8/10.
+ * Util to generate Agora media dynamic key.
+ */
+public class DynamicKey {
+    /**
+     * Generate Dynamic Key for media channel service
+     * @param appID App ID assigned by Agora
+     * @param appCertificate App Certificate assigned by Agora
+     * @param channelName name of channel to join, limited to 64 bytes and should be printable ASCII characters
+     * @param unixTs unix timestamp in seconds when generating the Dynamic Key
+     * @param randomInt salt for generating dynamic key
+     * @return String representation of dynamic key
+     * @throws Exception
+     */
+    public static String generate(String appID, String appCertificate, String channelName, int unixTs, int randomInt) throws Exception {
+        String unixTsStr = ("0000000000" + Integer.toString(unixTs)).substring(Integer.toString(unixTs).length());
+        String randomIntStr = ("00000000" + Integer.toHexString(randomInt)).substring(Integer.toHexString(randomInt).length());
+        String signature = generateSignature(appID, appCertificate, channelName, unixTsStr, randomIntStr);
+        return String.format("%s%s%s%s", signature, appID, unixTsStr, randomIntStr);
+    }
+
+    private static String generateSignature(String appID, String appCertificate, String channelName, String unixTsStr, String randomIntStr) throws Exception {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        baos.write(appID.getBytes());
+        baos.write(unixTsStr.getBytes());
+        baos.write(randomIntStr.getBytes());
+        baos.write(channelName.getBytes());
+        byte[] sign = DynamicKeyUtil.encodeHMAC(appCertificate, baos.toByteArray());
+        return DynamicKeyUtil.bytesToHex(sign);
+    }
+
+}

+ 40 - 0
src/main/java/com/fdkankan/tk/io/agora/media/DynamicKey3.java

@@ -0,0 +1,40 @@
+package com.fdkankan.tk.io.agora.media;
+
+import java.io.ByteArrayOutputStream;
+
+public class DynamicKey3 {
+
+    /**
+     * Manipulate Agora dynamic key for media connection.
+     *
+     * @param appID   App ID assigned by Agora when register
+     * @param appCertificate App Certificate assigned by Agora
+     * @param channelName name of channel to join
+     * @param unixTs      unix timestamp by seconds
+     * @param randomInt   random uint32 salt for generating dynamic key
+     * @return String representation of dynamic key to join Agora media server
+     * @throws Exception if any error occurs
+     */
+    public static String generate(String appID, String appCertificate, String channelName, int unixTs, int randomInt, long uid, int expiredTs) throws Exception {
+        String version = "003";
+        String unixTsStr = ("0000000000" + Integer.toString(unixTs)).substring(Integer.toString(unixTs).length());
+        String randomIntStr = ("00000000" + Integer.toHexString(randomInt)).substring(Integer.toHexString(randomInt).length());
+        uid = uid & 0xFFFFFFFFL;
+        String uidStr = ("0000000000" + Long.toString(uid)).substring(Long.toString(uid).length());
+        String expiredTsStr = ("0000000000" + Integer.toString(expiredTs)).substring(Integer.toString(expiredTs).length());
+        String signature = generateSignature3(appID, appCertificate, channelName, unixTsStr, randomIntStr, uidStr, expiredTsStr);
+        return String.format("%s%s%s%s%s%s%s", version, signature, appID, unixTsStr, randomIntStr, uidStr, expiredTsStr);
+    }
+
+    private static String generateSignature3(String appID, String appCertificate, String channelName, String unixTsStr, String randomIntStr, String uidStr, String expiredTsStr) throws Exception {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        baos.write(appID.getBytes());
+        baos.write(unixTsStr.getBytes());
+        baos.write(randomIntStr.getBytes());
+        baos.write(channelName.getBytes());
+        baos.write(uidStr.getBytes());
+        baos.write(expiredTsStr.getBytes());
+        byte[] sign = DynamicKeyUtil.encodeHMAC(appCertificate, baos.toByteArray());
+        return DynamicKeyUtil.bytesToHex(sign);
+    }
+}

+ 82 - 0
src/main/java/com/fdkankan/tk/io/agora/media/DynamicKey4.java

@@ -0,0 +1,82 @@
+package com.fdkankan.tk.io.agora.media;
+
+import java.io.ByteArrayOutputStream;
+
+public class DynamicKey4 {
+
+    private static final String PUBLIC_SHARING_SERVICE = "APSS";
+    private static final String RECORDING_SERVICE = "ARS";
+    private static final String MEDIA_CHANNEL_SERVICE = "ACS";
+    /**
+     * Generate Dynamic Key for Public Sharing Service
+     * @param appID App IDassigned by Agora
+     * @param appCertificate App Certificate assigned by Agora
+     * @param channelName name of channel to join, limited to 64 bytes and should be printable ASCII characters
+     * @param unixTs unix timestamp in seconds when generating the Dynamic Key
+     * @param randomInt salt for generating dynamic key
+     * @param uid user id, range from 0 - max uint32
+     * @param expiredTs should be 0
+     * @return String representation of dynamic key
+     * @throws Exception if any error occurs
+     */
+    public static String generatePublicSharingKey(String appID, String appCertificate, String channelName, int unixTs, int randomInt, long uid, int expiredTs) throws Exception {
+        return doGenerate(appID, appCertificate, channelName, unixTs, randomInt, uid, expiredTs, PUBLIC_SHARING_SERVICE);
+    }
+
+
+    /**
+     * Generate Dynamic Key for recording service
+     * @param appID Vendor key assigned by Agora
+     * @param appCertificate Sign key assigned by Agora
+     * @param channelName name of channel to join, limited to 64 bytes and should be printable ASCII characters
+     * @param unixTs unix timestamp in seconds when generating the Dynamic Key
+     * @param randomInt salt for generating dynamic key
+     * @param uid user id, range from 0 - max uint32
+     * @param expiredTs should be 0
+     * @return String representation of dynamic key
+     * @throws Exception if any error occurs
+     */
+    public static String generateRecordingKey(String appID, String appCertificate, String channelName, int unixTs, int randomInt, long uid, int expiredTs) throws Exception {
+        return doGenerate(appID, appCertificate, channelName, unixTs, randomInt, uid, expiredTs, RECORDING_SERVICE);
+    }
+
+    /**
+     * Generate Dynamic Key for media channel service
+     * @param appID Vendor key assigned by Agora
+     * @param appCertificate Sign key assigned by Agora
+     * @param channelName name of channel to join, limited to 64 bytes and should be printable ASCII characters
+     * @param unixTs unix timestamp in seconds when generating the Dynamic Key
+     * @param randomInt salt for generating dynamic key
+     * @param uid user id, range from 0 - max uint32
+     * @param expiredTs service expiring timestamp. After this timestamp, user will not be able to stay in the channel.
+     * @return String representation of dynamic key
+     * @throws Exception if any error occurs
+     */
+    public static String generateMediaChannelKey(String appID, String appCertificate, String channelName, int unixTs, int randomInt, long uid, int expiredTs) throws Exception {
+        return doGenerate(appID, appCertificate, channelName, unixTs, randomInt, uid, expiredTs, MEDIA_CHANNEL_SERVICE);
+    }
+
+    private static String doGenerate(String appID, String appCertificate, String channelName, int unixTs, int randomInt, long uid, int expiredTs, String serviceType) throws Exception {
+        String version = "004";
+        String unixTsStr = ("0000000000" + Integer.toString(unixTs)).substring(Integer.toString(unixTs).length());
+        String randomIntStr = ("00000000" + Integer.toHexString(randomInt)).substring(Integer.toHexString(randomInt).length());
+        uid = uid & 0xFFFFFFFFL;
+        String uidStr = ("0000000000" + Long.toString(uid)).substring(Long.toString(uid).length());
+        String expiredTsStr = ("0000000000" + Integer.toString(expiredTs)).substring(Integer.toString(expiredTs).length());
+        String signature = generateSignature4(appID, appCertificate, channelName, unixTsStr, randomIntStr, uidStr, expiredTsStr, serviceType);
+        return String.format("%s%s%s%s%s%s", version, signature, appID, unixTsStr, randomIntStr, expiredTsStr);
+    }
+
+    private static String generateSignature4(String appID, String appCertificate, String channelName, String unixTsStr, String randomIntStr, String uidStr, String expiredTsStr, String serviceType) throws Exception {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        baos.write(serviceType.getBytes());
+        baos.write(appID.getBytes());
+        baos.write(unixTsStr.getBytes());
+        baos.write(randomIntStr.getBytes());
+        baos.write(channelName.getBytes());
+        baos.write(uidStr.getBytes());
+        baos.write(expiredTsStr.getBytes());
+        byte[] sign = DynamicKeyUtil.encodeHMAC(appCertificate, baos.toByteArray());
+        return DynamicKeyUtil.bytesToHex(sign);
+    }
+}

+ 149 - 0
src/main/java/com/fdkankan/tk/io/agora/media/DynamicKey5.java

@@ -0,0 +1,149 @@
+package com.fdkankan.tk.io.agora.media;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.codec.binary.Hex;
+
+import java.util.TreeMap;
+
+/**
+ * Created by Li on 10/1/2016.
+ */
+public class DynamicKey5 {
+    public final static String version = "005";
+    public final static String noUpload = "0";
+    public final static String audioVideoUpload = "3";
+
+    // ServiceType
+    public final static short MEDIA_CHANNEL_SERVICE = 1;
+    public final static short RECORDING_SERVICE = 2;
+    public final static short PUBLIC_SHARING_SERVICE = 3;
+    public final static short IN_CHANNEL_PERMISSION = 4;
+
+    // InChannelPermissionKey
+    public final static short ALLOW_UPLOAD_IN_CHANNEL = 1;
+
+    public DynamicKey5Content content;
+
+    public boolean fromString(String key) {
+        if (! key.substring(0, 3).equals(version)) {
+            return false;
+        }
+
+        byte[] rawContent = new Base64().decode(key.substring(3));
+        if (rawContent.length == 0) {
+            return false;
+        }
+
+        content = new DynamicKey5Content();
+        ByteBuf buffer = new ByteBuf(rawContent);
+        content.unmarshall(buffer);
+        return true;
+    }
+
+    public static String generateSignature(String appCertificate, short service, String appID, int unixTs, int salt, String channelName, long uid, int expiredTs, TreeMap<Short, String> extra) throws Exception {
+        // decode hex to avoid case problem
+        Hex hex = new Hex();
+        byte[] rawAppID = hex.decode(appID.getBytes());
+        byte[] rawAppCertificate = hex.decode(appCertificate.getBytes());
+
+        Message m = new Message(service, rawAppID, unixTs, salt, channelName, (int)(uid & 0xFFFFFFFFL), expiredTs, extra);
+        byte[] toSign = pack(m);
+        return new String(Hex.encodeHex(DynamicKeyUtil.encodeHMAC(rawAppCertificate, toSign), false));
+    }
+
+    public static String generateDynamicKey(String appID, String appCertificate, String channel, int ts, int salt, long uid, int expiredTs, TreeMap<Short, String> extra, short service) throws Exception {
+        String signature = generateSignature(appCertificate, service, appID, ts, salt, channel, uid, expiredTs, extra);
+        DynamicKey5Content content = new DynamicKey5Content(service, signature, new Hex().decode(appID.getBytes()), ts, salt, expiredTs, extra);
+        byte[] bytes = pack(content);
+        byte[] encoded = new Base64().encode(bytes);
+        String base64 = new String(encoded);
+        return version + base64;
+    }
+
+    private static byte[] pack(Packable content) {
+        ByteBuf buffer = new ByteBuf();
+        content.marshal(buffer);
+        return buffer.asBytes();
+    }
+
+    public static String generatePublicSharingKey(String appID, String appCertificate, String channel, int ts, int salt, long uid, int expiredTs) throws Exception {
+        return generateDynamicKey(appID, appCertificate, channel, ts, salt, uid, expiredTs, new TreeMap<Short, String>(), PUBLIC_SHARING_SERVICE);
+    }
+
+    public static String generateRecordingKey(String appID, String appCertificate, String channel, int ts, int salt, long uid, int expiredTs) throws Exception {
+        return generateDynamicKey(appID, appCertificate, channel, ts, salt, uid, expiredTs, new TreeMap<Short, String>(), RECORDING_SERVICE);
+    }
+
+    public static String generateMediaChannelKey(String appID, String appCertificate, String channel, int ts, int salt, long uid, int expiredTs) throws Exception {
+        return generateDynamicKey(appID, appCertificate, channel, ts, salt, uid, expiredTs, new TreeMap<Short, String>(), MEDIA_CHANNEL_SERVICE);
+    }
+
+    public static String generateInChannelPermissionKey(String appID, String appCertificate, String channel, int ts, int salt, long uid, int expiredTs, String permission) throws Exception {
+        TreeMap<Short, String> extra = new TreeMap<Short, String>();
+        extra.put(ALLOW_UPLOAD_IN_CHANNEL, permission);
+        return generateDynamicKey(appID, appCertificate, channel, ts, salt, uid, expiredTs, extra, IN_CHANNEL_PERMISSION);
+    }
+
+    static class Message implements Packable {
+        public short serviceType;
+        public byte[] appID;
+        public int unixTs;
+        public int salt;
+        public String channelName;
+        public int uid;
+        public int expiredTs;
+        public TreeMap<Short, String> extra;
+
+        public Message(short serviceType, byte[] appID, int unixTs, int salt, String channelName, int uid, int expiredTs, TreeMap<Short, String> extra) {
+            this.serviceType = serviceType;
+            this.appID = appID;
+            this.unixTs = unixTs;
+            this.salt = salt;
+            this.channelName = channelName;
+            this.uid = uid;
+            this.expiredTs = expiredTs;
+            this.extra = extra;
+        }
+
+        public ByteBuf marshal(ByteBuf out) {
+            return out.put(serviceType).put(appID).put(unixTs).put(salt).put(channelName).put(uid).put(expiredTs).put(extra);
+        }
+    }
+
+    public static class DynamicKey5Content implements Packable {
+        public short serviceType;
+        public String signature;
+        public byte[] appID;
+        public int unixTs;
+        public int salt;
+        public int expiredTs;
+        public TreeMap<Short, String> extra;
+
+        public DynamicKey5Content() {
+        }
+
+        public DynamicKey5Content(short serviceType, String signature, byte[] appID, int unixTs, int salt, int expiredTs, TreeMap<Short, String> extra) {
+            this.serviceType = serviceType;
+            this.signature = signature;
+            this.appID = appID;
+            this.unixTs = unixTs;
+            this.salt = salt;
+            this.expiredTs = expiredTs;
+            this.extra = extra;
+        }
+
+        public ByteBuf marshal(ByteBuf out) {
+            return out.put(serviceType).put(signature).put(appID).put(unixTs).put(salt).put(expiredTs).put(extra);
+        }
+
+        public void unmarshall(ByteBuf in) {
+            this.serviceType = in.readShort();
+            this.signature = in.readString();
+            this.appID = in.readBytes();
+            this.unixTs = in.readInt();
+            this.salt = in.readInt();
+            this.expiredTs = in.readInt();
+            this.extra = in.readMap();
+        }
+    }
+}

+ 33 - 0
src/main/java/com/fdkankan/tk/io/agora/media/DynamicKeyUtil.java

@@ -0,0 +1,33 @@
+package com.fdkankan.tk.io.agora.media;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * Created by hefeng on 15/8/10.
+ * Util to generate Agora media dynamic key.
+ */
+public class DynamicKeyUtil {
+
+    static byte[] encodeHMAC(String key, byte[] message) throws NoSuchAlgorithmException, InvalidKeyException {
+        return encodeHMAC(key.getBytes(), message);
+    }
+
+    static byte[] encodeHMAC(byte[] key, byte[] message) throws NoSuchAlgorithmException, InvalidKeyException {
+        SecretKeySpec keySpec = new SecretKeySpec(key, "HmacSHA1");
+
+        Mac mac = Mac.getInstance("HmacSHA1");
+        mac.init(keySpec);
+        return mac.doFinal(message);
+    }
+
+    static String bytesToHex(byte[] in) {
+        final StringBuilder builder = new StringBuilder();
+        for (byte b : in) {
+            builder.append(String.format("%02x", b));
+        }
+        return builder.toString();
+    }
+}

+ 27 - 0
src/main/java/com/fdkankan/tk/io/agora/media/FpaTokenBuilder.java

@@ -0,0 +1,27 @@
+package com.fdkankan.tk.io.agora.media;
+
+public class FpaTokenBuilder {
+    /**
+     * Build the FPA token.
+     *
+     * @param appId:          The App ID issued to you by Agora. Apply for a new App ID from
+     *                        Agora Dashboard if it is missing from your kit. See Get an App ID.
+     * @param appCertificate: Certificate of the application that you registered in
+     *                        the Agora Dashboard. See Get an App Certificate.
+     * @return The FPA token.
+     */
+    public String buildToken(String appId, String appCertificate) {
+        AccessToken2 accessToken = new AccessToken2(appId, appCertificate, 24 * 3600);
+        AccessToken2.Service serviceFpa = new AccessToken2.ServiceFpa();
+
+        serviceFpa.addPrivilegeFpa(AccessToken2.PrivilegeFpa.PRIVILEGE_LOGIN, 0);
+        accessToken.addService(serviceFpa);
+
+        try {
+            return accessToken.build();
+        } catch (Exception e) {
+            e.printStackTrace();
+            return "";
+        }
+    }
+}

+ 8 - 0
src/main/java/com/fdkankan/tk/io/agora/media/Packable.java

@@ -0,0 +1,8 @@
+package com.fdkankan.tk.io.agora.media;
+
+/**
+ * Created by Li on 10/1/2016.
+ */
+public interface Packable {
+    ByteBuf marshal(ByteBuf out);
+}

+ 5 - 0
src/main/java/com/fdkankan/tk/io/agora/media/PackableEx.java

@@ -0,0 +1,5 @@
+package com.fdkankan.tk.io.agora.media;
+
+public interface PackableEx extends Packable {
+    void unmarshal(ByteBuf in);
+}

+ 108 - 0
src/main/java/com/fdkankan/tk/io/agora/media/RtcTokenBuilder.java

@@ -0,0 +1,108 @@
+package com.fdkankan.tk.io.agora.media;
+
+public class RtcTokenBuilder {
+	public enum Role {
+        /**
+         * DEPRECATED. Role_Attendee has the same privileges as Role_Publisher.
+         */
+        Role_Attendee(0),
+        /**
+         *    RECOMMENDED. Use this role for a voice/video call or a live broadcast, if your scenario does not require authentication for [Hosting-in](https://docs.agora.io/en/Agora%20Platform/terms?platform=All%20Platforms#hosting-in).
+         */
+        Role_Publisher(1), 
+        /**
+         * Only use this role if your scenario require authentication for [Hosting-in](https://docs.agora.io/en/Agora%20Platform/terms?platform=All%20Platforms#hosting-in).
+         *
+         * @note In order for this role to take effect, please contact our support team to enable authentication for Hosting-in for you. Otherwise, Role_Subscriber still has the same privileges as Role_Publisher.
+         */
+        Role_Subscriber(2), 
+        /**
+         * DEPRECATED. Role_Attendee has the same privileges as Role_Publisher.
+         */
+        Role_Admin(101);
+
+        public int initValue;
+
+        Role(int initValue) {
+            this.initValue = initValue;
+        }
+    }
+
+    /**
+     * Builds an RTC token using an int uid.
+     *
+     * @param appId The App ID issued to you by Agora. 
+     * @param appCertificate Certificate of the application that you registered in 
+     *        the Agora Dashboard. 
+     * @param channelName The unique channel name for the AgoraRTC session in the string format. The string length must be less than 64 bytes. Supported character scopes are:
+     * <ul>
+     *    <li> The 26 lowercase English letters: a to z.</li>
+     *    <li> The 26 uppercase English letters: A to Z.</li>
+     *    <li> The 10 digits: 0 to 9.</li>
+     *    <li> The space.</li>
+     *    <li> "!", "#", "$", "%", "&", "(", ")", "+", "-", ":", ";", "<", "=", ".", ">", "?", "@", "[", "]", "^", "_", " {", "}", "|", "~", ",".
+     * </ul>
+     * @param uid  User ID. A 32-bit unsigned integer with a value ranging from 
+     *        1 to (2^32-1).
+     * @param role The user role.
+     * <ul>
+     *     <li> Role_Publisher = 1: RECOMMENDED. Use this role for a voice/video call or a live broadcast.</li>
+     *     <li> Role_Subscriber = 2: ONLY use this role if your live-broadcast scenario requires authentication for [Hosting-in](https://docs.agora.io/en/Agora%20Platform/terms?platform=All%20Platforms#hosting-in). In order for this role to take effect, please contact our support team to enable authentication for Hosting-in for you. Otherwise, Role_Subscriber still has the same privileges as Role_Publisher.</li>
+     * </ul>
+     * @param privilegeTs Represented by the number of seconds elapsed since 1/1/1970.
+     *        If, for example, you want to access the Agora Service within 10 minutes
+     *        after the token is generated, set expireTimestamp as the current time stamp
+     *        + 600 (seconds).                             
+     */
+    public String buildTokenWithUid(String appId, String appCertificate, 
+    		String channelName, int uid, Role role, int privilegeTs) {
+    	String account = uid == 0 ? "" : String.valueOf(uid);
+    	return buildTokenWithUserAccount(appId, appCertificate, channelName, 
+    			account, role, privilegeTs);
+    }
+    
+    /**
+     * Builds an RTC token using a string userAccount.
+     * 
+     * @param appId The App ID issued to you by Agora. 
+     * @param appCertificate Certificate of the application that you registered in 
+     *        the Agora Dashboard. 
+     * @param channelName The unique channel name for the AgoraRTC session in the string format. The string length must be less than 64 bytes. Supported character scopes are:
+     * <ul>
+     *    <li> The 26 lowercase English letters: a to z.</li>
+     *    <li> The 26 uppercase English letters: A to Z.</li>
+     *    <li> The 10 digits: 0 to 9.</li>
+     *    <li> The space.</li>
+     *    <li> "!", "#", "$", "%", "&", "(", ")", "+", "-", ":", ";", "<", "=", ".", ">", "?", "@", "[", "]", "^", "_", " {", "}", "|", "~", ",".
+     * </ul>
+     * @param account  The user account.
+     * @param role The user role.
+     * <ul>
+     *     <li> Role_Publisher = 1: RECOMMENDED. Use this role for a voice/video call or a live broadcast.</li>
+     *     <li> Role_Subscriber = 2: ONLY use this role if your live-broadcast scenario requires authentication for [Hosting-in](https://docs.agora.io/en/Agora%20Platform/terms?platform=All%20Platforms#hosting-in). In order for this role to take effect, please contact our support team to enable authentication for Hosting-in for you. Otherwise, Role_Subscriber still has the same privileges as Role_Publisher.</li>
+     * </ul>
+     * @param privilegeTs represented by the number of seconds elapsed since 1/1/1970.
+     *        If, for example, you want to access the Agora Service within 10 minutes
+     *        after the token is generated, set expireTimestamp as the current time stamp
+     *        + 600 (seconds).                             
+     */
+    public String buildTokenWithUserAccount(String appId, String appCertificate, 
+    		String channelName, String account, Role role, int privilegeTs) {
+    	
+    	// Assign appropriate access privileges to each role.
+    	AccessToken builder = new AccessToken(appId, appCertificate, channelName, account);
+    	builder.addPrivilege(AccessToken.Privileges.kJoinChannel, privilegeTs);
+    	if (role == Role.Role_Publisher || role == Role.Role_Subscriber || role == Role.Role_Admin) {
+    		builder.addPrivilege(AccessToken.Privileges.kPublishAudioStream, privilegeTs);
+    		builder.addPrivilege(AccessToken.Privileges.kPublishVideoStream, privilegeTs);
+    		builder.addPrivilege(AccessToken.Privileges.kPublishDataStream, privilegeTs);
+    	}
+    	
+    	try {
+			return builder.build();
+		} catch (Exception e) {
+			e.printStackTrace();
+			return "";
+		}
+    }
+}

+ 203 - 0
src/main/java/com/fdkankan/tk/io/agora/media/RtcTokenBuilder2.java

@@ -0,0 +1,203 @@
+package com.fdkankan.tk.io.agora.media;
+
+public class RtcTokenBuilder2 {
+    public enum Role {
+        ROLE_PUBLISHER(1),
+        ROLE_SUBSCRIBER(2),
+        ;
+
+        public int initValue;
+
+        Role(int initValue) {
+            this.initValue = initValue;
+        }
+    }
+
+    /**
+     * Build the RTC token with uid.
+     *
+     * @param appId:            The App ID issued to you by Agora. Apply for a new App ID from
+     *                          Agora Dashboard if it is missing from your kit. See Get an App ID.
+     * @param appCertificate:   Certificate of the application that you registered in
+     *                          the Agora Dashboard. See Get an App Certificate.
+     * @param channelName:      Unique channel name for the AgoraRTC session in the string format
+     * @param uid:              User ID. A 32-bit unsigned integer with a value ranging from 1 to (2^32-1).
+     *                          optionalUid must be unique.
+     * @param role:             ROLE_PUBLISHER: A broadcaster/host in a live-broadcast profile.
+     *                          ROLE_SUBSCRIBER: An audience(default) in a live-broadcast profile.
+     * @param token_expire:     represented by the number of seconds elapsed since now. If, for example,
+     *                          you want to access the Agora Service within 10 minutes after the token is generated,
+     *                          set token_expire as 600(seconds).
+     * @param privilege_expire: represented by the number of seconds elapsed since now. If, for example,
+     *                          you want to enable your privilege for 10 minutes, set privilege_expire as 600(seconds).
+     * @return The RTC token.
+     */
+    public String buildTokenWithUid(String appId, String appCertificate, String channelName, int uid, Role role, int token_expire, int privilege_expire) {
+        return buildTokenWithUserAccount(appId, appCertificate, channelName, AccessToken2.getUidStr(uid), role, token_expire, privilege_expire);
+    }
+
+    /**
+     * Build the RTC token with account.
+     *
+     * @param appId:            The App ID issued to you by Agora. Apply for a new App ID from
+     *                          Agora Dashboard if it is missing from your kit. See Get an App ID.
+     * @param appCertificate:   Certificate of the application that you registered in
+     *                          the Agora Dashboard. See Get an App Certificate.
+     * @param channelName:      Unique channel name for the AgoraRTC session in the string format
+     * @param account:          The user's account, max length is 255 Bytes.
+     * @param role:             ROLE_PUBLISHER: A broadcaster/host in a live-broadcast profile.
+     *                          ROLE_SUBSCRIBER: An audience(default) in a live-broadcast profile.
+     * @param token_expire:     represented by the number of seconds elapsed since now. If, for example,
+     *                          you want to access the Agora Service within 10 minutes after the token is generated,
+     *                          set token_expire as 600(seconds).
+     * @param privilege_expire: represented by the number of seconds elapsed since now. If, for example,
+     *                          you want to enable your privilege for 10 minutes, set privilege_expire as 600(seconds).
+     * @return The RTC token.
+     */
+    public String buildTokenWithUserAccount(String appId, String appCertificate, String channelName, String account, Role role, int token_expire, int privilege_expire) {
+        AccessToken2 accessToken = new AccessToken2(appId, appCertificate, token_expire);
+        AccessToken2.Service serviceRtc = new AccessToken2.ServiceRtc(channelName, account);
+
+        serviceRtc.addPrivilegeRtc(AccessToken2.PrivilegeRtc.PRIVILEGE_JOIN_CHANNEL, privilege_expire);
+        if (role == Role.ROLE_PUBLISHER) {
+            serviceRtc.addPrivilegeRtc(AccessToken2.PrivilegeRtc.PRIVILEGE_PUBLISH_AUDIO_STREAM, privilege_expire);
+            serviceRtc.addPrivilegeRtc(AccessToken2.PrivilegeRtc.PRIVILEGE_PUBLISH_VIDEO_STREAM, privilege_expire);
+            serviceRtc.addPrivilegeRtc(AccessToken2.PrivilegeRtc.PRIVILEGE_PUBLISH_DATA_STREAM, privilege_expire);
+        }
+        accessToken.addService(serviceRtc);
+
+        try {
+            return accessToken.build();
+        } catch (Exception e) {
+            e.printStackTrace();
+            return "";
+        }
+    }
+
+    /**
+     * Generates an RTC token with the specified privilege.
+     * <p>
+     * This method supports generating a token with the following privileges:
+     * - Joining an RTC channel.
+     * - Publishing audio in an RTC channel.
+     * - Publishing video in an RTC channel.
+     * - Publishing data streams in an RTC channel.
+     * <p>
+     * The privileges for publishing audio, video, and data streams in an RTC channel apply only if you have
+     * enabled co-host authentication.
+     * <p>
+     * A user can have multiple privileges. Each privilege is valid for a maximum of 24 hours.
+     * The SDK triggers the onTokenPrivilegeWillExpire and onRequestToken callbacks when the token is about to expire
+     * or has expired. The callbacks do not report the specific privilege affected, and you need to maintain
+     * the respective timestamp for each privilege in your app logic. After receiving the callback, you need
+     * to generate a new token, and then call renewToken to pass the new token to the SDK, or call joinChannel to re-join
+     * the channel.
+     *
+     * @param appId                        The App ID of your Agora project.
+     * @param appCertificate               The App Certificate of your Agora project.
+     * @param channelName                  The unique channel name for the Agora RTC session in string format. The string length must be less than 64 bytes. The channel name may contain the following characters:
+     *                                     - All lowercase English letters: a to z.
+     *                                     - All uppercase English letters: A to Z.
+     *                                     - All numeric characters: 0 to 9.
+     *                                     - The space character.
+     *                                     - "!", "#", "$", "%", "&", "(", ")", "+", "-", ":", ";", "<", "=", ".", ">", "?", "@", "[", "]", "^", "_", " {", "}", "|", "~", ",".
+     * @param uid                          The user ID. A 32-bit unsigned integer with a value range from 1 to (2^32 - 1). It must be unique. Set uid as 0, if you do not want to authenticate the user ID, that is, any uid from the app client can join the channel.
+     * @param tokenExpire                  represented by the number of seconds elapsed since now. If, for example, you want to access the
+     *                                     Agora Service within 10 minutes after the token is generated, set tokenExpire as 600(seconds).
+     * @param joinChannelPrivilegeExpire   The Unix timestamp when the privilege for joining the channel expires, represented
+     *                                     by the sum of the current timestamp plus the valid time period of the token. For example, if you set joinChannelPrivilegeExpire as the
+     *                                     current timestamp plus 600 seconds, the token expires in 10 minutes.
+     * @param pubAudioPrivilegeExpire      The Unix timestamp when the privilege for publishing audio expires, represented
+     *                                     by the sum of the current timestamp plus the valid time period of the token. For example, if you set pubAudioPrivilegeExpire as the
+     *                                     current timestamp plus 600 seconds, the token expires in 10 minutes. If you do not want to enable this privilege,
+     *                                     set pubAudioPrivilegeExpire as the current Unix timestamp.
+     * @param pubVideoPrivilegeExpire      The Unix timestamp when the privilege for publishing video expires, represented
+     *                                     by the sum of the current timestamp plus the valid time period of the token. For example, if you set pubVideoPrivilegeExpire as the
+     *                                     current timestamp plus 600 seconds, the token expires in 10 minutes. If you do not want to enable this privilege,
+     *                                     set pubVideoPrivilegeExpire as the current Unix timestamp.
+     * @param pubDataStreamPrivilegeExpire The Unix timestamp when the privilege for publishing data streams expires, represented
+     *                                     by the sum of the current timestamp plus the valid time period of the token. For example, if you set pubDataStreamPrivilegeExpire as the
+     *                                     current timestamp plus 600 seconds, the token expires in 10 minutes. If you do not want to enable this privilege,
+     *                                     set pubDataStreamPrivilegeExpire as the current Unix timestamp.
+     * @note Agora recommends setting a reasonable timestamp for each privilege according to your scenario.
+     * Suppose the expiration timestamp for joining the channel is set earlier than that for publishing audio.
+     * When the token for joining the channel expires, the user is immediately kicked off the RTC channel
+     * and cannot publish any audio stream, even though the timestamp for publishing audio has not expired.
+     */
+    public String buildTokenWithUid(String appId, String appCertificate, String channelName, int uid,
+                                    int tokenExpire, int joinChannelPrivilegeExpire, int pubAudioPrivilegeExpire,
+                                    int pubVideoPrivilegeExpire, int pubDataStreamPrivilegeExpire) {
+        return buildTokenWithUserAccount(appId, appCertificate, channelName, AccessToken2.getUidStr(uid),
+                tokenExpire, joinChannelPrivilegeExpire, pubAudioPrivilegeExpire, pubVideoPrivilegeExpire, pubDataStreamPrivilegeExpire);
+    }
+
+    /**
+     * Generates an RTC token with the specified privilege.
+     * <p>
+     * This method supports generating a token with the following privileges:
+     * - Joining an RTC channel.
+     * - Publishing audio in an RTC channel.
+     * - Publishing video in an RTC channel.
+     * - Publishing data streams in an RTC channel.
+     * <p>
+     * The privileges for publishing audio, video, and data streams in an RTC channel apply only if you have
+     * enabled co-host authentication.
+     * <p>
+     * A user can have multiple privileges. Each privilege is valid for a maximum of 24 hours.
+     * The SDK triggers the onTokenPrivilegeWillExpire and onRequestToken callbacks when the token is about to expire
+     * or has expired. The callbacks do not report the specific privilege affected, and you need to maintain
+     * the respective timestamp for each privilege in your app logic. After receiving the callback, you need
+     * to generate a new token, and then call renewToken to pass the new token to the SDK, or call joinChannel to re-join
+     * the channel.
+     *
+     * @param appId                        The App ID of your Agora project.
+     * @param appCertificate               The App Certificate of your Agora project.
+     * @param channelName                  The unique channel name for the Agora RTC session in string format. The string length must be less than 64 bytes. The channel name may contain the following characters:
+     *                                     - All lowercase English letters: a to z.
+     *                                     - All uppercase English letters: A to Z.
+     *                                     - All numeric characters: 0 to 9.
+     *                                     - The space character.
+     *                                     - "!", "#", "$", "%", "&", "(", ")", "+", "-", ":", ";", "<", "=", ".", ">", "?", "@", "[", "]", "^", "_", " {", "}", "|", "~", ",".
+     * @param account                      The user account.
+     * @param tokenExpire                  represented by the number of seconds elapsed since now. If, for example, you want to access the
+     *                                     Agora Service within 10 minutes after the token is generated, set tokenExpire as 600(seconds).
+     * @param joinChannelPrivilegeExpire   The Unix timestamp when the privilege for joining the channel expires, represented
+     *                                     by the sum of the current timestamp plus the valid time period of the token. For example, if you set joinChannelPrivilegeExpire as the
+     *                                     current timestamp plus 600 seconds, the token expires in 10 minutes.
+     * @param pubAudioPrivilegeExpire      The Unix timestamp when the privilege for publishing audio expires, represented
+     *                                     by the sum of the current timestamp plus the valid time period of the token. For example, if you set pubAudioPrivilegeExpire as the
+     *                                     current timestamp plus 600 seconds, the token expires in 10 minutes. If you do not want to enable this privilege,
+     *                                     set pubAudioPrivilegeExpire as the current Unix timestamp.
+     * @param pubVideoPrivilegeExpire      The Unix timestamp when the privilege for publishing video expires, represented
+     *                                     by the sum of the current timestamp plus the valid time period of the token. For example, if you set pubVideoPrivilegeExpire as the
+     *                                     current timestamp plus 600 seconds, the token expires in 10 minutes. If you do not want to enable this privilege,
+     *                                     set pubVideoPrivilegeExpire as the current Unix timestamp.
+     * @param pubDataStreamPrivilegeExpire The Unix timestamp when the privilege for publishing data streams expires, represented
+     *                                     by the sum of the current timestamp plus the valid time period of the token. For example, if you set pubDataStreamPrivilegeExpire as the
+     *                                     current timestamp plus 600 seconds, the token expires in 10 minutes. If you do not want to enable this privilege,
+     *                                     set pubDataStreamPrivilegeExpire as the current Unix timestamp.
+     * @note Agora recommends setting a reasonable timestamp for each privilege according to your scenario.
+     * Suppose the expiration timestamp for joining the channel is set earlier than that for publishing audio.
+     * When the token for joining the channel expires, the user is immediately kicked off the RTC channel
+     * and cannot publish any audio stream, even though the timestamp for publishing audio has not expired.
+     */
+    public String buildTokenWithUserAccount(String appId, String appCertificate, String channelName, String account,
+                                            int tokenExpire, int joinChannelPrivilegeExpire, int pubAudioPrivilegeExpire,
+                                            int pubVideoPrivilegeExpire, int pubDataStreamPrivilegeExpire) {
+        AccessToken2 accessToken = new AccessToken2(appId, appCertificate, tokenExpire);
+        AccessToken2.Service serviceRtc = new AccessToken2.ServiceRtc(channelName, account);
+
+        serviceRtc.addPrivilegeRtc(AccessToken2.PrivilegeRtc.PRIVILEGE_JOIN_CHANNEL, joinChannelPrivilegeExpire);
+        serviceRtc.addPrivilegeRtc(AccessToken2.PrivilegeRtc.PRIVILEGE_PUBLISH_AUDIO_STREAM, pubAudioPrivilegeExpire);
+        serviceRtc.addPrivilegeRtc(AccessToken2.PrivilegeRtc.PRIVILEGE_PUBLISH_VIDEO_STREAM, pubVideoPrivilegeExpire);
+        serviceRtc.addPrivilegeRtc(AccessToken2.PrivilegeRtc.PRIVILEGE_PUBLISH_DATA_STREAM, pubDataStreamPrivilegeExpire);
+        accessToken.addService(serviceRtc);
+
+        try {
+            return accessToken.build();
+        } catch (Exception e) {
+            e.printStackTrace();
+            return "";
+        }
+    }
+}

+ 140 - 0
src/main/java/com/fdkankan/tk/io/agora/media/Utils.java

@@ -0,0 +1,140 @@
+package com.fdkankan.tk.io.agora.media;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.apache.commons.codec.binary.Base64;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.Date;
+import java.util.zip.CRC32;
+import java.util.zip.Deflater;
+import java.util.zip.Inflater;
+
+public class Utils {
+    public static final long HMAC_SHA256_LENGTH = 32;
+    public static final int VERSION_LENGTH = 3;
+    public static final int APP_ID_LENGTH = 32;
+
+    public static byte[] hmacSign(String keyString, byte[] msg) throws InvalidKeyException, NoSuchAlgorithmException {
+        SecretKeySpec keySpec = new SecretKeySpec(keyString.getBytes(), "HmacSHA256");
+        Mac mac = Mac.getInstance("HmacSHA256");
+        mac.init(keySpec);
+        return mac.doFinal(msg);
+    }
+
+    public static byte[] pack(PackableEx packableEx) {
+        ByteBuf buffer = new ByteBuf();
+        packableEx.marshal(buffer);
+        return buffer.asBytes();
+    }
+
+    public static void unpack(byte[] data, PackableEx packableEx) {
+        ByteBuf buffer = new ByteBuf(data);
+        packableEx.unmarshal(buffer);
+    }
+
+    public static String base64Encode(byte[] data) {
+        byte[] encodedBytes = Base64.encodeBase64(data);
+        return new String(encodedBytes);
+    }
+
+    public static byte[] base64Decode(String data) {
+        return Base64.decodeBase64(data.getBytes());
+    }
+
+    public static int crc32(String data) {
+        // get bytes from string
+        byte[] bytes = data.getBytes();
+        return crc32(bytes);
+    }
+
+    public static int crc32(byte[] bytes) {
+        CRC32 checksum = new CRC32();
+        checksum.update(bytes);
+        return (int)checksum.getValue();
+    }
+
+    public static int getTimestamp() {
+        return (int)((new Date().getTime())/1000);
+    }
+
+    public static int randomInt() {
+        return new SecureRandom().nextInt();
+    }
+
+    public static boolean isUUID(String uuid) {
+        if (uuid.length() != 32) {
+            return false;
+        }
+
+        return uuid.matches("\\p{XDigit}+");
+    }
+
+    public static byte[] compress(byte[] data) {
+        byte[] output;
+        Deflater deflater = new Deflater();
+        ByteArrayOutputStream bos = new ByteArrayOutputStream(data.length);
+
+        try {
+            deflater.reset();
+            deflater.setInput(data);
+            deflater.finish();
+
+            byte[] buf = new byte[data.length];
+            while (!deflater.finished()) {
+                int i = deflater.deflate(buf);
+                bos.write(buf, 0, i);
+            }
+            output = bos.toByteArray();
+        } catch (Exception e) {
+            output = data;
+            e.printStackTrace();
+        } finally {
+            deflater.end();
+        }
+
+        return output;
+    }
+
+    public static byte[] decompress(byte[] data) {
+        Inflater inflater = new Inflater();
+        ByteArrayOutputStream bos = new ByteArrayOutputStream(data.length);
+
+        try {
+            inflater.setInput(data);
+            byte[] buf = new byte[data.length];
+            while (!inflater.finished()) {
+                int i = inflater.inflate(buf);
+                bos.write(buf, 0, i);
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            inflater.end();
+        }
+
+        return bos.toByteArray();
+    }
+
+    public static String md5(String plainText) {
+        byte[] secretBytes = null;
+        try {
+            secretBytes = MessageDigest.getInstance("md5").digest(
+                    plainText.getBytes());
+        } catch (NoSuchAlgorithmException e) {
+            throw new RuntimeException("No md5 digest!");
+        }
+        String md5code = new BigInteger(1, secretBytes).toString(16);
+        for (int i = 0; i < 32 - md5code.length(); i++) {
+            md5code = "0" + md5code;
+        }
+        return md5code;
+    }
+}

+ 33 - 0
src/main/java/com/fdkankan/tk/io/agora/rtm/RtmTokenBuilder.java

@@ -0,0 +1,33 @@
+package com.fdkankan.tk.io.agora.rtm;
+
+
+import com.fdkankan.tk.io.agora.media.AccessToken;
+
+public class RtmTokenBuilder {
+    public enum Role {
+        Rtm_User(1);
+
+        int value;
+        Role(int value) {
+            this.value = value;
+        }
+    }
+
+    public AccessToken mTokenCreator;
+
+    public String buildToken(String appId, String appCertificate,
+            String uid, Role role, int privilegeTs) throws Exception {
+        mTokenCreator = new AccessToken(appId, appCertificate, uid, "");
+        mTokenCreator.addPrivilege(AccessToken.Privileges.kRtmLogin, privilegeTs);
+        return mTokenCreator.build();
+    }
+
+    public void setPrivilege(AccessToken.Privileges privilege, int expireTs) {
+        mTokenCreator.addPrivilege(privilege, expireTs);
+    }
+
+    public boolean initTokenBuilder(String originToken) {
+        mTokenCreator.fromString(originToken);
+        return true;
+    }
+}

+ 34 - 0
src/main/java/com/fdkankan/tk/io/agora/rtm/RtmTokenBuilder2.java

@@ -0,0 +1,34 @@
+package com.fdkankan.tk.io.agora.rtm;
+
+
+import com.fdkankan.tk.io.agora.media.AccessToken2;
+
+public class RtmTokenBuilder2 {
+
+    /**
+     * Build the RTM token.
+     *
+     * @param appId:          The App ID issued to you by Agora. Apply for a new App ID from
+     *                        Agora Dashboard if it is missing from your kit. See Get an App ID.
+     * @param appCertificate: Certificate of the application that you registered in
+     *                        the Agora Dashboard. See Get an App Certificate.
+     * @param userId:         The user's account, max length is 64 Bytes.
+     * @param expire:         represented by the number of seconds elapsed since now. If, for example, you want to access the
+     *                        Agora Service within 10 minutes after the token is generated, set expireTimestamp as 600(seconds).
+     * @return The RTM token.
+     */
+    public String buildToken(String appId, String appCertificate, String userId, int expire) {
+        AccessToken2 accessToken = new AccessToken2(appId, appCertificate, expire);
+        AccessToken2.Service serviceRtm = new AccessToken2.ServiceRtm(userId);
+
+        serviceRtm.addPrivilegeRtm(AccessToken2.PrivilegeRtm.PRIVILEGE_LOGIN, expire);
+        accessToken.addService(serviceRtm);
+
+        try {
+            return accessToken.build();
+        } catch (Exception e) {
+            e.printStackTrace();
+            return "";
+        }
+    }
+}

+ 93 - 0
src/main/java/com/fdkankan/tk/io/agora/sample/AccessTokenInspector.java

@@ -0,0 +1,93 @@
+package com.fdkankan.tk.io.agora.sample;
+
+
+import com.fdkankan.tk.io.agora.media.AccessToken2;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.TreeMap;
+
+public class AccessTokenInspector {
+    private static String token = "007eJxTYBBbsMMnKq7p9Hf/HcIX5kce9b518kCiQgSr5Zrp4X1Tu6UUGCzNDZwdjU1TUs0Mkk1MzExMk5ISUy0SjQxNDcwMk4yN3b8IMEQwMTAwMoAwBIL4CgzmKeZGxmamqUmWFsYmFqbGluapxqnGaZYpJmYGSSkpiVwMRhYWRsYmhkbmxgDCaiTj";
+
+    public static void main(String[] args) {
+        AccessTokenInspector inspector = new AccessTokenInspector();
+        inspector.inspect(token);
+    }
+
+    void inspect(String input) {
+        AccessToken2 token = new AccessToken2();
+        System.out.printf("parsing token: %s\n\n", input);
+        token.parse(input);
+        System.out.printf("appId:%s\n", token.appId);
+        System.out.printf("appCert:%s\n", token.appCert);
+        System.out.printf("salt:%d\n", token.salt);
+        System.out.printf("issueTs:%d\n", token.issueTs);
+        System.out.printf("expire:%d\n", token.expire);
+        System.out.printf("services:\n");
+
+        for (AccessToken2.Service service : token.services.values()) {
+            System.out.printf("\t{%s}\n", toServiceStr(service));
+        }
+    }
+
+    String toServiceStr(AccessToken2.Service service) {
+        if (service.getServiceType() == AccessToken2.SERVICE_TYPE_RTC) {
+            AccessToken2.ServiceRtc serviceRtc = (AccessToken2.ServiceRtc) service;
+            return String.format("type:rtc, channel:%s, uid: %s, privileges: [%s]}", serviceRtc.getChannelName(),
+                    serviceRtc.getUid(), toRtcPrivileges(serviceRtc.getPrivileges()));
+        } else if (service.getServiceType() == AccessToken2.SERVICE_TYPE_RTM) {
+            AccessToken2.ServiceRtm serviceRtm = (AccessToken2.ServiceRtm) service;
+            return String.format("type:rtm, user_id:%s, privileges:[%s]", serviceRtm.getUserId(),
+                    toRtmPrivileges(serviceRtm.getPrivileges()));
+        } else if (service.getServiceType() == AccessToken2.SERVICE_TYPE_CHAT) {
+            AccessToken2.ServiceChat serviceChat = (AccessToken2.ServiceChat) service;
+            return String.format("type:chat, user_id:%s, privileges:[%s]", serviceChat.getUserId(),
+                    toChatPrivileges(serviceChat.getPrivileges()));
+        }
+        return "unknown";
+    }
+
+    private String toRtcPrivileges(TreeMap<Short, Integer> privileges) {
+        List<String> privilegeStrList = new ArrayList<>(privileges.size());
+        if (privileges.containsKey(AccessToken2.PrivilegeRtc.PRIVILEGE_JOIN_CHANNEL.intValue)) {
+            privilegeStrList.add(String.format("JOIN_CHANNEL(%d)",
+                    privileges.get(AccessToken2.PrivilegeRtc.PRIVILEGE_JOIN_CHANNEL.intValue)));
+        }
+        if (privileges.containsKey(AccessToken2.PrivilegeRtc.PRIVILEGE_PUBLISH_AUDIO_STREAM.intValue)) {
+            privilegeStrList.add(String.format("PUBLISH_AUDIO_STREAM(%d)",
+                    privileges.get(AccessToken2.PrivilegeRtc.PRIVILEGE_PUBLISH_AUDIO_STREAM.intValue)));
+        }
+        if (privileges.containsKey(AccessToken2.PrivilegeRtc.PRIVILEGE_PUBLISH_VIDEO_STREAM.intValue)) {
+            privilegeStrList.add(String.format("PUBLISH_VIDEO_STREAM(%d)",
+                    privileges.get(AccessToken2.PrivilegeRtc.PRIVILEGE_PUBLISH_VIDEO_STREAM.intValue)));
+        }
+        if (privileges.containsKey(AccessToken2.PrivilegeRtc.PRIVILEGE_PUBLISH_DATA_STREAM.intValue)) {
+            privilegeStrList.add(String.format("PUBLISH_DATA_STREAM(%d)",
+                    privileges.get(AccessToken2.PrivilegeRtc.PRIVILEGE_PUBLISH_DATA_STREAM.intValue)));
+        }
+        return String.join(",", privilegeStrList);
+    }
+
+    private String toRtmPrivileges(TreeMap<Short, Integer> privileges) {
+        List<String> privilegeStrList = new ArrayList<>(privileges.size());
+        if (privileges.containsKey(AccessToken2.PrivilegeRtm.PRIVILEGE_LOGIN.intValue)) {
+            privilegeStrList.add(String.format("JOIN_LOGIN(%d)",
+                    privileges.get(AccessToken2.PrivilegeRtm.PRIVILEGE_LOGIN.intValue)));
+        }
+        return String.join(",", privilegeStrList);
+    }
+
+    private String toChatPrivileges(TreeMap<Short, Integer> privileges) {
+        List<String> privilegeStrList = new ArrayList<>(privileges.size());
+        if (privileges.containsKey(AccessToken2.PrivilegeChat.PRIVILEGE_CHAT_USER.intValue)) {
+            privilegeStrList.add(String.format("USER(%d)",
+                    privileges.get(AccessToken2.PrivilegeChat.PRIVILEGE_CHAT_USER.intValue)));
+        }
+        if (privileges.containsKey(AccessToken2.PrivilegeChat.PRIVILEGE_CHAT_APP.intValue)) {
+            privilegeStrList.add(String.format("APP(%d)",
+                    privileges.get(AccessToken2.PrivilegeChat.PRIVILEGE_CHAT_APP.intValue)));
+        }
+        return String.join(",", privilegeStrList);
+    }
+}

+ 19 - 0
src/main/java/com/fdkankan/tk/io/agora/sample/ChatTokenBuilder2Sample.java

@@ -0,0 +1,19 @@
+package com.fdkankan.tk.io.agora.sample;
+
+
+import com.fdkankan.tk.io.agora.chat.ChatTokenBuilder2;
+
+public class ChatTokenBuilder2Sample {
+    private static String appId = "970CA35de60c44645bbae8a215061b33";
+    private static String appCertificate = "5CFd2fd1755d40ecb72977518be15d3b";
+    private static String userId = "2882341273";
+    private static int expire = 600;
+
+    public static void main(String[] args) throws Exception {
+    	ChatTokenBuilder2 tokenBuilder = new ChatTokenBuilder2();
+        String userToken = tokenBuilder.buildUserToken(appId, appCertificate, userId, expire);
+        System.out.printf("Chat user token: %s\n", userToken);
+        String appToken = tokenBuilder.buildAppToken(appId, appCertificate, expire);
+        System.out.printf("Chat app token: %s\n", appToken);
+    }
+}

+ 27 - 0
src/main/java/com/fdkankan/tk/io/agora/sample/DynamicKey5Sample.java

@@ -0,0 +1,27 @@
+package com.fdkankan.tk.io.agora.sample;
+
+
+import com.fdkankan.tk.io.agora.media.DynamicKey5;
+
+import java.util.Date;
+import java.util.Random;
+
+/**
+ * Created by Li on 10/1/2016.
+ */
+public class DynamicKey5Sample {
+    static String appID = "970ca35de60c44645bbae8a215061b33";
+    static String appCertificate = "5cfd2fd1755d40ecb72977518be15d3b";
+    static String channel = "7d72365eb983485397e3e3f9d460bdda";
+    static int ts = (int)(new Date().getTime()/1000);
+    static int r = new Random().nextInt();
+    static long uid = 2882341273L;
+    static int expiredTs = 0;
+
+    public static void main(String[] args) throws Exception {
+        System.out.println(DynamicKey5.generateMediaChannelKey(appID, appCertificate, channel, ts, r, uid, expiredTs));
+        System.out.println(DynamicKey5.generateRecordingKey(appID, appCertificate, channel, ts, r, uid, expiredTs));
+        System.out.println(DynamicKey5.generateInChannelPermissionKey(appID, appCertificate, channel, ts, r, uid, expiredTs, DynamicKey5.noUpload));
+        System.out.println(DynamicKey5.generateInChannelPermissionKey(appID, appCertificate, channel, ts, r, uid, expiredTs, DynamicKey5.audioVideoUpload));
+    }
+}

+ 25 - 0
src/main/java/com/fdkankan/tk/io/agora/sample/EducationTokenBuilder2Sample.java

@@ -0,0 +1,25 @@
+package com.fdkankan.tk.io.agora.sample;
+
+
+import com.fdkankan.tk.io.agora.education.EducationTokenBuilder2;
+
+public class EducationTokenBuilder2Sample {
+    private static String appId = "970CA35de60c44645bbae8a215061b33";
+    private static String appCertificate = "5CFd2fd1755d40ecb72977518be15d3b";
+    private static String roomUuid = "123";
+    private static String userUuid = "2882341273";
+    private static Short role = 1;
+    private static int expire = 600;
+
+    public static void main(String[] args) {
+        EducationTokenBuilder2 tokenBuilder = new EducationTokenBuilder2();
+        String roomUserToken = tokenBuilder.buildRoomUserToken(appId, appCertificate, roomUuid, userUuid, role, expire);
+        System.out.printf("Education room user token: %s\n", roomUserToken);
+
+        String userToken = tokenBuilder.buildUserToken(appId, appCertificate, userUuid, expire);
+        System.out.printf("Education user token: %s\n", userToken);
+
+        String appToken = tokenBuilder.buildAppToken(appId, appCertificate, expire);
+        System.out.printf("Education app token: %s\n", appToken);
+    }
+}

+ 15 - 0
src/main/java/com/fdkankan/tk/io/agora/sample/FpaTokenBuilderSample.java

@@ -0,0 +1,15 @@
+package com.fdkankan.tk.io.agora.sample;
+
+
+import com.fdkankan.tk.io.agora.media.FpaTokenBuilder;
+
+public class FpaTokenBuilderSample {
+    static String appId = "970CA35de60c44645bbae8a215061b33";
+    static String appCertificate = "5CFd2fd1755d40ecb72977518be15d3b";
+
+    public static void main(String[] args) {
+        FpaTokenBuilder token = new FpaTokenBuilder();
+        String result = token.buildToken(appId, appCertificate);
+        System.out.println("Token with FPA service: " + result);
+    }
+}

+ 29 - 0
src/main/java/com/fdkankan/tk/io/agora/sample/RtcTokenBuilder2Sample.java

@@ -0,0 +1,29 @@
+package com.fdkankan.tk.io.agora.sample;
+
+
+import com.fdkankan.tk.io.agora.media.RtcTokenBuilder2;
+
+public class RtcTokenBuilder2Sample {
+    static String appId = "dceec50eb3874a23b6bb6bf722455da4";
+    static String appCertificate = "d9c16154cb8a4b6e9acd7135b8017026";
+    static String channelName = "test001";
+    static String account = "066893f525e56f09ce8b4420dff7e068";
+    static int uid = 0;
+    static int tokenExpirationInSeconds = 3600;
+    static int privilegeExpirationInSeconds = 3600;
+
+    public static void main(String[] args) {
+        RtcTokenBuilder2 token = new RtcTokenBuilder2();
+        String result = token.buildTokenWithUid(appId, appCertificate, channelName, uid, RtcTokenBuilder2.Role.ROLE_SUBSCRIBER, tokenExpirationInSeconds, privilegeExpirationInSeconds);
+        System.out.println("Token with uid: " + result);
+
+        result = token.buildTokenWithUserAccount(appId, appCertificate, channelName, account, RtcTokenBuilder2.Role.ROLE_SUBSCRIBER, tokenExpirationInSeconds, privilegeExpirationInSeconds);
+        System.out.println("Token with account: " + result);
+
+        result = token.buildTokenWithUid(appId, appCertificate, channelName, uid, privilegeExpirationInSeconds, privilegeExpirationInSeconds, privilegeExpirationInSeconds, privilegeExpirationInSeconds, privilegeExpirationInSeconds);
+        System.out.println("Token with uid and privilege: " + result);
+
+        result = token.buildTokenWithUserAccount(appId, appCertificate, channelName, account, privilegeExpirationInSeconds, privilegeExpirationInSeconds, privilegeExpirationInSeconds, privilegeExpirationInSeconds, privilegeExpirationInSeconds);
+        System.out.println("Token with account and privilege: " + result);
+    }
+}

+ 25 - 0
src/main/java/com/fdkankan/tk/io/agora/sample/RtcTokenBuilderSample.java

@@ -0,0 +1,25 @@
+package com.fdkankan.tk.io.agora.sample;
+
+
+import com.fdkankan.tk.io.agora.media.RtcTokenBuilder;
+
+public class RtcTokenBuilderSample {
+    static String appId = "970CA35de60c44645bbae8a215061b33";
+    static String appCertificate = "5CFd2fd1755d40ecb72977518be15d3b";
+    static String channelName = "7d72365eb983485397e3e3f9d460bdda";
+    static String userAccount = "2082341273";
+    static int uid = 2082341273;
+    static int expirationTimeInSeconds = 3600; 
+
+    public static void main(String[] args) throws Exception {
+        RtcTokenBuilder token = new RtcTokenBuilder();
+        int timestamp = (int)(System.currentTimeMillis() / 1000 + expirationTimeInSeconds);
+        String result = token.buildTokenWithUserAccount(appId, appCertificate,  
+        		 channelName, userAccount, RtcTokenBuilder.Role.Role_Publisher, timestamp);
+        System.out.println(result);
+        
+        result = token.buildTokenWithUid(appId, appCertificate,  
+       		 channelName, uid, RtcTokenBuilder.Role.Role_Publisher, timestamp);
+        System.out.println(result);
+    }
+}

+ 17 - 0
src/main/java/com/fdkankan/tk/io/agora/sample/RtmTokenBuilder2Sample.java

@@ -0,0 +1,17 @@
+package com.fdkankan.tk.io.agora.sample;
+
+
+import com.fdkankan.tk.io.agora.rtm.RtmTokenBuilder2;
+
+public class RtmTokenBuilder2Sample {
+    private static String appId = "dceec50eb3874a23b6bb6bf722455da4";
+    private static String appCertificate = "d9c16154cb8a4b6e9acd7135b8017026";
+    private static String userId = "test_user_id";
+    private static int expirationInSeconds = 3600;
+
+    public static void main(String[] args) {
+        RtmTokenBuilder2 token = new RtmTokenBuilder2();
+        String result = token.buildToken(appId, appCertificate, userId, expirationInSeconds);
+        System.out.println("Rtm Token: " + result);
+    }
+}

+ 17 - 0
src/main/java/com/fdkankan/tk/io/agora/sample/RtmTokenBuilderSample.java

@@ -0,0 +1,17 @@
+package com.fdkankan.tk.io.agora.sample;
+
+
+import com.fdkankan.tk.io.agora.rtm.RtmTokenBuilder;
+
+public class RtmTokenBuilderSample {
+    private static String appId = "970CA35de60c44645bbae8a215061b33";
+    private static String appCertificate = "5CFd2fd1755d40ecb72977518be15d3b";
+    private static String userId = "2882341273";
+    private static int expireTimestamp = 0;
+
+    public static void main(String[] args) throws Exception {
+    	RtmTokenBuilder token = new RtmTokenBuilder();
+        String result = token.buildToken(appId, appCertificate, userId, RtmTokenBuilder.Role.Rtm_User, expireTimestamp);
+        System.out.println(result);
+    }
+}

+ 22 - 0
src/main/java/com/fdkankan/tk/io/agora/sample/SignalingTokenSample.java

@@ -0,0 +1,22 @@
+package com.fdkankan.tk.io.agora.sample;
+
+
+import com.fdkankan.tk.io.agora.signal.SignalingToken;
+
+import java.security.NoSuchAlgorithmException;
+import java.util.Date;
+
+public class SignalingTokenSample {
+
+    public static void main(String []args) throws NoSuchAlgorithmException{
+
+        String appId = "970ca35de60c44645bbae8a215061b33";
+        String certificate = "5cfd2fd1755d40ecb72977518be15d3b";
+        String account = "TestAccount";
+        //Use the current time plus an available time to guarantee the only time it is obtained
+        int expiredTsInSeconds = 1446455471 + (int) (new Date().getTime()/1000l);
+        String result = SignalingToken.getToken(appId, certificate, account, expiredTsInSeconds);
+        System.out.println(result);
+
+    }
+}

+ 90 - 0
src/main/java/com/fdkankan/tk/io/agora/sample/Verifier5.java

@@ -0,0 +1,90 @@
+package com.fdkankan.tk.io.agora.sample;
+
+import com.fdkankan.tk.io.agora.media.DynamicKey5;
+import org.apache.commons.codec.binary.Hex;
+
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * Created by liwei on 8/2/17.
+ */
+public class Verifier5 {
+    public static void main(String[] args) throws Exception {
+        if (args.length < 5) {
+            System.out.println("java io.agora.media.sample.Verifier5 appID appCertificate channelName uid channelKey");
+            return;
+        }
+
+        String appID = args[0];
+        String appCertificate = args[1];
+        String channelName = args[2];
+        int uid = Integer.parseInt(args[3]);
+        String channelKey = args[4];
+
+        DynamicKey5 key5 = new DynamicKey5();
+        if (! key5.fromString(channelKey)) {
+            System.out.println("Faile to parse key");
+            return ;
+        }
+
+        System.out.println("signature " + key5.content.signature);
+        System.out.println("appID     " + new String(Hex.encodeHex(key5.content.appID, false)));
+        System.out.println("unixTs    " + key5.content.unixTs);
+        System.out.println("randomInt " + key5.content.salt);
+        System.out.println("expiredTs " + key5.content.expiredTs);
+        System.out.println("extra     [" + toString(key5.content.extra) + "]");
+        System.out.println("service   " + key5.content.serviceType);
+
+        System.out.println();
+        System.out.println("Original \t\t " + channelKey);
+
+        if (key5.content.serviceType == DynamicKey5.MEDIA_CHANNEL_SERVICE) {
+            System.out.println("Uid = 0 \t\t " + DynamicKey5.generateMediaChannelKey(appID, appCertificate, channelName, key5.content.unixTs, key5.content.salt, 0, key5.content.expiredTs));
+            System.out.println("Uid =  " + uid + " \t " + DynamicKey5.generateMediaChannelKey(appID, appCertificate, channelName, key5.content.unixTs, key5.content.salt, uid, key5.content.expiredTs));
+        } else if (key5.content.serviceType == DynamicKey5.RECORDING_SERVICE) {
+            System.out.println("Uid = 0 \t\t " + DynamicKey5.generateRecordingKey(appID, appCertificate, channelName, key5.content.unixTs, key5.content.salt, 0, key5.content.expiredTs));
+            System.out.println("Uid =  " + uid + " \t " + DynamicKey5.generateRecordingKey(appID, appCertificate, channelName, key5.content.unixTs, key5.content.salt, uid, key5.content.expiredTs));
+        } else if (key5.content.serviceType == DynamicKey5.IN_CHANNEL_PERMISSION) {
+            String permission = key5.content.extra.get(DynamicKey5.ALLOW_UPLOAD_IN_CHANNEL);
+            if (permission != DynamicKey5.noUpload && permission != DynamicKey5.audioVideoUpload) {
+                System.out.println("Unknown in channel upload permission " + permission + " in extra [" + toString(key5.content.extra) + "]");
+                return ;
+            }
+            System.out.println("Uid = 0 \t\t " + DynamicKey5.generateInChannelPermissionKey(appID, appCertificate, channelName, key5.content.unixTs, key5.content.salt, 0, key5.content.expiredTs, permission));
+            System.out.println("Uid =  " + uid + " \t " + DynamicKey5.generateInChannelPermissionKey(appID, appCertificate, channelName, key5.content.unixTs, key5.content.salt, uid, key5.content.expiredTs, permission));
+        } else {
+            System.out.println("Unknown service type " + key5.content.serviceType);
+        }
+
+        String signature = DynamicKey5.generateSignature(appCertificate,
+                key5.content.serviceType,
+                appID,
+                key5.content.unixTs,
+                key5.content.salt,
+                channelName,
+                uid,
+                0,
+                key5.content.extra
+        );
+        System.out.println("generated signature " + signature);
+    }
+
+    private static String toString(TreeMap<Short, String> extra) {
+        String s = "";
+
+        String separator = "";
+
+        for (Map.Entry<Short,String> v : extra.entrySet()) {
+            s += separator;
+            s += v.getKey();
+            s += ":";
+            s += v.getValue();
+            separator = ", ";
+        }
+
+        return s;
+    }
+
+
+}

+ 34 - 0
src/main/java/com/fdkankan/tk/io/agora/signal/SignalingToken.java

@@ -0,0 +1,34 @@
+package com.fdkankan.tk.io.agora.signal;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Date;
+
+public class SignalingToken {
+
+    public static String getToken(String appId, String certificate, String account, int expiredTsInSeconds) throws NoSuchAlgorithmException {
+
+        StringBuilder digest_String = new StringBuilder().append(account).append(appId).append(certificate).append(expiredTsInSeconds);
+        MessageDigest md5 = MessageDigest.getInstance("MD5");
+        md5.update(digest_String.toString().getBytes());
+        byte[] output = md5.digest();
+        String token = hexlify(output);
+        String token_String = new StringBuilder().append("1").append(":").append(appId).append(":").append(expiredTsInSeconds).append(":").append(token).toString();
+        return token_String;
+    }
+
+    public static String hexlify(byte[] data) {
+
+        char[] DIGITS_LOWER = {'0', '1', '2', '3', '4', '5',
+                '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+        char[] toDigits = DIGITS_LOWER;
+        int l = data.length;
+        char[] out = new char[l << 1];
+        // two characters form the hex value.
+        for (int i = 0, j = 0; i < l; i++) {
+            out[j++] = toDigits[(0xF0 & data[i]) >>> 4];
+            out[j++] = toDigits[0x0F & data[i]];
+        }
+        return String.valueOf(out);
+    }
+}

+ 17 - 8
src/main/java/com/fdkankan/tk/service/impl/TencentYunServiceImpl.java

@@ -7,11 +7,12 @@ import com.fdkankan.tk.common.ResultCode;
 import com.fdkankan.tk.common.util.RedisKeyUtil;
 import com.fdkankan.tk.common.util.RedisKeyUtil;
 import com.fdkankan.tk.entity.TencentYun;
 import com.fdkankan.tk.entity.TencentYun;
 import com.fdkankan.tk.exception.BusinessException;
 import com.fdkankan.tk.exception.BusinessException;
+import com.fdkankan.tk.io.agora.media.RtcTokenBuilder;
+import com.fdkankan.tk.io.agora.media.RtcTokenBuilder2;
 import com.fdkankan.tk.mapper.ITencentYunMapper;
 import com.fdkankan.tk.mapper.ITencentYunMapper;
 import com.fdkankan.tk.service.ITencentYunService;
 import com.fdkankan.tk.service.ITencentYunService;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.tencentyun.TLSSigAPIv2;
 import com.tencentyun.TLSSigAPIv2;
-import io.agora.media.RtcTokenBuilder;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.stereotype.Service;
 
 
@@ -70,12 +71,10 @@ public class TencentYunServiceImpl extends ServiceImpl<ITencentYunMapper, Tencen
     }
     }
 
 
 
 
-    static HashMap<Integer,RtcTokenBuilder.Role> roleMap = new HashMap<>();
+    static HashMap<Integer, RtcTokenBuilder2.Role> roleMap = new HashMap<>();
     private void setRoleMap (){
     private void setRoleMap (){
-        roleMap.put(RtcTokenBuilder.Role.Role_Admin.initValue,RtcTokenBuilder.Role.Role_Admin);
-        roleMap.put(RtcTokenBuilder.Role.Role_Publisher.initValue,RtcTokenBuilder.Role.Role_Publisher);
-        roleMap.put(RtcTokenBuilder.Role.Role_Subscriber.initValue,RtcTokenBuilder.Role.Role_Subscriber);
-        roleMap.put(RtcTokenBuilder.Role.Role_Attendee.initValue,RtcTokenBuilder.Role.Role_Attendee);
+        roleMap.put(RtcTokenBuilder2.Role.ROLE_PUBLISHER.initValue,RtcTokenBuilder2.Role.ROLE_PUBLISHER);
+        roleMap.put(RtcTokenBuilder2.Role.ROLE_SUBSCRIBER.initValue,RtcTokenBuilder2.Role.ROLE_SUBSCRIBER);
     }
     }
 
 
     @Override
     @Override
@@ -96,8 +95,10 @@ public class TencentYunServiceImpl extends ServiceImpl<ITencentYunMapper, Tencen
                 throw new BusinessException(ResultCode.TENCENT_YUN_ERROR);
                 throw new BusinessException(ResultCode.TENCENT_YUN_ERROR);
             }
             }
             TencentYun tencentYun = list.get(0);
             TencentYun tencentYun = list.get(0);
-            RtcTokenBuilder token = new RtcTokenBuilder();
-            String result = token.buildTokenWithUserAccount(tencentYun.getAppid(), tencentYun.getSecretkey(), channelName, userId,roleMap.get(roleId) ,tencentYun.getExTime());
+
+            RtcTokenBuilder2 token = new RtcTokenBuilder2();
+            String result = token.buildTokenWithUserAccount(tencentYun.getAppid(), tencentYun.getSecretkey(), channelName, userId, roleMap.get(roleId), tencentYun.getExTime(), tencentYun.getExTime());
+
             JSONObject jsonObject = new JSONObject();
             JSONObject jsonObject = new JSONObject();
             jsonObject.put("sdkAppId",tencentYun.getAppid());
             jsonObject.put("sdkAppId",tencentYun.getAppid());
             jsonObject.put("expire",tencentYun.getExTime());
             jsonObject.put("expire",tencentYun.getExTime());
@@ -111,6 +112,14 @@ public class TencentYunServiceImpl extends ServiceImpl<ITencentYunMapper, Tencen
         return jsonObject;
         return jsonObject;
     }
     }
 
 
+    public static void main(String[] args) {
+        RtcTokenBuilder2 token = new RtcTokenBuilder2();
+
+        String result = token.buildTokenWithUid("dceec50eb3874a23b6bb6bf722455da4", "d9c16154cb8a4b6e9acd7135b8017026", "test001",
+                0,   RtcTokenBuilder2.Role.ROLE_PUBLISHER ,3600,3600);
+        System.out.println(result);
+
+    }
 
 
 
 
 }
 }